| @@ -1,6 +1,6 @@ | |||
| #include "./build.hpp" | |||
| #include <dds/compile.hpp> | |||
| #include <dds/build/compile.hpp> | |||
| #include <dds/logging.hpp> | |||
| #include <dds/proc.hpp> | |||
| #include <dds/project.hpp> | |||
| @@ -50,15 +50,6 @@ auto iter_libraries(const project& pr) { | |||
| return libs; | |||
| } | |||
| fs::path object_file_path(fs::path source_path, const build_params& params) { | |||
| auto obj_dir = params.out_root / "obj"; | |||
| auto obj_relpath = fs::relative(source_path, params.root); | |||
| obj_relpath.replace_filename(obj_relpath.filename().string() | |||
| + params.toolchain.object_suffix()); | |||
| auto obj_path = obj_dir / obj_relpath; | |||
| return obj_path; | |||
| } | |||
| fs::path lib_archive_path(const build_params& params, const library& lib) { | |||
| return params.out_root | |||
| / (fmt::format("lib{}{}", lib.name(), params.toolchain.archive_suffix())); | |||
| @@ -183,8 +174,8 @@ void include_deps(const lm::index::library_index& lib_index, | |||
| } | |||
| } | |||
| std::vector<file_compilation> file_compilations_of_lib(const build_params& params, | |||
| const library& lib) { | |||
| std::vector<compile_file_plan> file_compilations_of_lib(const build_params& params, | |||
| const library& lib) { | |||
| const auto& sources = lib.sources(); | |||
| std::vector<fs::path> dep_includes; | |||
| @@ -224,30 +215,26 @@ std::vector<file_compilation> file_compilations_of_lib(const build_params& param | |||
| || (sf.kind == source_kind::test && params.build_tests)); | |||
| }; | |||
| compilation_rules rules; | |||
| rules.base_path() = lib.base_dir() / "src"; | |||
| shared_compile_file_rules rules; | |||
| extend(rules.defs(), lib.manifest().private_defines); | |||
| extend(rules.defs(), dep_defines); | |||
| extend(rules.include_dirs(), lib.manifest().private_includes); | |||
| extend(rules.include_dirs(), dep_includes); | |||
| rules.include_dirs().push_back(fs::absolute(lib.base_dir() / "src")); | |||
| rules.include_dirs().push_back(fs::absolute(lib.base_dir() / "include")); | |||
| rules.enable_warnings() = params.enable_warnings; | |||
| return // | |||
| sources // | |||
| | filter(should_compile_source) // | |||
| | transform([&](auto&& src) { | |||
| return file_compilation{rules, | |||
| src, | |||
| object_file_path(src.path, params), | |||
| lib.name(), | |||
| params.enable_warnings}; | |||
| return compile_file_plan{rules, src, lib.name()}; | |||
| }) // | |||
| | to_vector; | |||
| } | |||
| std::vector<dds::file_compilation> collect_compiles(const build_params& params, | |||
| const project& project) { | |||
| std::vector<dds::compile_file_plan> collect_compiles(const build_params& params, | |||
| const project& project) { | |||
| auto libs = iter_libraries(project); | |||
| return // | |||
| libs // | |||
| @@ -401,13 +388,16 @@ link_project_lib(const build_params& params, const library& lib, const object_fi | |||
| return res; | |||
| } | |||
| std::vector<link_results> link_project(const build_params& params, | |||
| const project& pr, | |||
| const std::vector<file_compilation>& compilations) { | |||
| auto obj_index = // | |||
| ranges::views::all(compilations) // | |||
| | transform([](auto&& comp) { return std::pair(comp.source.path, comp.obj); }) // | |||
| | ranges::to<object_file_index>() // | |||
| std::vector<link_results> link_project(const build_params& params, | |||
| const project& pr, | |||
| const std::vector<compile_file_plan>& compilations) { | |||
| auto obj_index = // | |||
| ranges::views::all(compilations) // | |||
| | transform([&](const compile_file_plan& comp) -> std::pair<fs::path, fs::path> { | |||
| return std::pair(comp.source.path, | |||
| params.out_root / comp.get_object_file_path(params.toolchain)); | |||
| }) // | |||
| | ranges::to<object_file_index>() // | |||
| ; | |||
| auto libs = iter_libraries(pr); | |||
| @@ -423,7 +413,7 @@ void dds::build(const build_params& params, const package_manifest&) { | |||
| auto compiles = collect_compiles(params, project); | |||
| dds::execute_all(compiles, params.toolchain, params.parallel_jobs); | |||
| dds::execute_all(compiles, params.toolchain, params.parallel_jobs, params.out_root); | |||
| using namespace ranges::views; | |||
| @@ -1,29 +1,13 @@ | |||
| #ifndef DDS_BUILD_HPP_INCLUDED | |||
| #define DDS_BUILD_HPP_INCLUDED | |||
| #pragma once | |||
| #include <dds/build/params.hpp> | |||
| #include <dds/package_manifest.hpp> | |||
| #include <dds/toolchain.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| #include <optional> | |||
| namespace dds { | |||
| struct build_params { | |||
| fs::path root; | |||
| fs::path out_root; | |||
| fs::path lm_index; | |||
| dds::toolchain toolchain; | |||
| bool do_export = false; | |||
| bool build_tests = false; | |||
| bool enable_warnings = false; | |||
| bool build_apps = false; | |||
| bool build_deps = false; | |||
| int parallel_jobs = 0; | |||
| }; | |||
| void build(const build_params&, const package_manifest& man); | |||
| } // namespace dds | |||
| #endif // DDS_BUILD_HPP_INCLUDED | |||
| @@ -0,0 +1 @@ | |||
| #include "./archive.hpp" | |||
| @@ -0,0 +1,17 @@ | |||
| #pragma once | |||
| #include <dds/toolchain.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| #include <vector> | |||
| namespace dds { | |||
| struct archive_rules { | |||
| std::vector<fs::path> objects; | |||
| fs::path out; | |||
| void create_archive(const toolchain& tc) const; | |||
| }; | |||
| } // namespace dds | |||
| @@ -13,16 +13,17 @@ | |||
| using namespace dds; | |||
| void file_compilation::compile(const toolchain& tc) const { | |||
| fs::create_directories(obj.parent_path()); | |||
| void compile_file_plan::compile(const toolchain& tc, path_ref out_prefix) const { | |||
| const auto obj_path = out_prefix / get_object_file_path(tc); | |||
| fs::create_directories(obj_path.parent_path()); | |||
| spdlog::info("[{}] Compile: {}", | |||
| owner_name, | |||
| fs::relative(source.path, rules.base_path()).string()); | |||
| qualifier, | |||
| fs::relative(source.path, source.basis_path).string()); | |||
| auto start_time = std::chrono::steady_clock::now(); | |||
| compile_file_spec spec{source.path, obj}; | |||
| spec.enable_warnings = enable_warnings; | |||
| compile_file_spec spec{source.path, obj_path}; | |||
| spec.enable_warnings = rules.enable_warnings(); | |||
| extend(spec.include_dirs, rules.include_dirs()); | |||
| extend(spec.definitions, rules.defs()); | |||
| @@ -34,8 +35,8 @@ void file_compilation::compile(const toolchain& tc) const { | |||
| auto dur_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time); | |||
| spdlog::info("[{}] Compile: {} - {:n}ms", | |||
| owner_name, | |||
| fs::relative(source.path, rules.base_path()).string(), | |||
| qualifier, | |||
| fs::relative(source.path, source.basis_path).string(), | |||
| dur_ms.count()); | |||
| if (!compile_res.okay()) { | |||
| @@ -57,9 +58,16 @@ void file_compilation::compile(const toolchain& tc) const { | |||
| } | |||
| } | |||
| void dds::execute_all(const std::vector<file_compilation>& compilations, | |||
| const toolchain& tc, | |||
| int n_jobs) { | |||
| fs::path compile_file_plan::get_object_file_path(const toolchain& tc) const noexcept { | |||
| auto relpath = fs::relative(source.path, source.basis_path); | |||
| relpath.replace_filename(relpath.filename().string() + tc.object_suffix()); | |||
| return relpath; | |||
| } | |||
| void dds::execute_all(const std::vector<compile_file_plan>& compilations, | |||
| const toolchain& tc, | |||
| int n_jobs, | |||
| path_ref out_prefix) { | |||
| // We don't bother with a nice thread pool, as the overhead of compiling | |||
| // source files dwarfs the cost of interlocking. | |||
| std::mutex mut; | |||
| @@ -81,7 +89,7 @@ void dds::execute_all(const std::vector<file_compilation>& compilations, | |||
| auto& compilation = *comp_iter++; | |||
| lk.unlock(); | |||
| try { | |||
| compilation.compile(tc); | |||
| compilation.compile(tc, out_prefix); | |||
| cancellation_point(); | |||
| } catch (...) { | |||
| lk.lock(); | |||
| @@ -112,4 +120,4 @@ void dds::execute_all(const std::vector<file_compilation>& compilations, | |||
| if (!exceptions.empty()) { | |||
| throw compile_failure("Failed to compile library sources"); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| #pragma once | |||
| #include <dds/source.hpp> | |||
| #include <dds/toolchain.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| #include <memory> | |||
| #include <optional> | |||
| #include <stdexcept> | |||
| namespace dds { | |||
| struct compile_failure : std::runtime_error { | |||
| using runtime_error::runtime_error; | |||
| }; | |||
| struct shared_compile_file_rules { | |||
| struct rules_impl { | |||
| std::vector<fs::path> inc_dirs; | |||
| std::vector<std::string> defs; | |||
| bool enable_warnings = false; | |||
| }; | |||
| std::shared_ptr<rules_impl> _impl = std::make_shared<rules_impl>(); | |||
| public: | |||
| shared_compile_file_rules() = default; | |||
| auto& include_dirs() noexcept { return _impl->inc_dirs; } | |||
| auto& include_dirs() const noexcept { return _impl->inc_dirs; } | |||
| auto& defs() noexcept { return _impl->defs; } | |||
| auto& defs() const noexcept { return _impl->defs; } | |||
| auto& enable_warnings() noexcept { return _impl->enable_warnings; } | |||
| auto& enable_warnings() const noexcept { return _impl->enable_warnings; } | |||
| }; | |||
| struct compile_file_plan { | |||
| shared_compile_file_rules rules; | |||
| dds::source_file source; | |||
| std::string qualifier; | |||
| fs::path get_object_file_path(const toolchain& tc) const noexcept; | |||
| void compile(const toolchain& tc, path_ref out_prefix) const; | |||
| }; | |||
| void execute_all(const std::vector<compile_file_plan>&, | |||
| const toolchain& tc, | |||
| int n_jobs, | |||
| path_ref out_prefix); | |||
| } // namespace dds | |||
| @@ -0,0 +1,14 @@ | |||
| #pragma once | |||
| #include <dds/util/fs.hpp> | |||
| #include <vector> | |||
| namespace dds { | |||
| class link_executable_rules { | |||
| std::vector<fs::path> inputs; | |||
| fs::path output; | |||
| }; | |||
| } // namespace dds | |||
| @@ -0,0 +1,21 @@ | |||
| #pragma once | |||
| #include <dds/toolchain.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| namespace dds { | |||
| struct build_params { | |||
| fs::path root; | |||
| fs::path out_root; | |||
| fs::path lm_index; | |||
| dds::toolchain toolchain; | |||
| bool do_export = false; | |||
| bool build_tests = false; | |||
| bool enable_warnings = false; | |||
| bool build_apps = false; | |||
| bool build_deps = false; | |||
| int parallel_jobs = 0; | |||
| }; | |||
| } // namespace dds | |||
| @@ -0,0 +1,144 @@ | |||
| #include "./plan.hpp" | |||
| #include <range/v3/action/join.hpp> | |||
| #include <range/v3/view/filter.hpp> | |||
| #include <range/v3/view/join.hpp> | |||
| #include <range/v3/view/repeat_n.hpp> | |||
| #include <range/v3/view/transform.hpp> | |||
| #include <range/v3/view/zip.hpp> | |||
| #include <spdlog/spdlog.h> | |||
| #include <mutex> | |||
| #include <thread> | |||
| using namespace dds; | |||
| void build_plan::add_sroot(const sroot& root, const sroot_build_params& params) { | |||
| create_libraries.push_back(library_plan::create(root, params)); | |||
| } | |||
| library_plan library_plan::create(const sroot& root, const sroot_build_params& params) { | |||
| std::vector<compile_file_plan> compile_files; | |||
| std::vector<create_archive_plan> create_archives; | |||
| std::vector<create_exe_plan> link_executables; | |||
| std::vector<source_file> app_sources; | |||
| std::vector<source_file> test_sources; | |||
| std::vector<source_file> lib_sources; | |||
| auto src_dir = root.src_dir(); | |||
| if (src_dir.exists()) { | |||
| auto all_sources = src_dir.sources(); | |||
| auto to_compile = all_sources | ranges::views::filter([&](const source_file& sf) { | |||
| return (sf.kind == source_kind::source | |||
| || (sf.kind == source_kind::app && params.build_apps) | |||
| || (sf.kind == source_kind::test && params.build_tests)); | |||
| }); | |||
| for (const auto& sfile : to_compile) { | |||
| compile_file_plan cf_plan; | |||
| cf_plan.source = sfile; | |||
| cf_plan.qualifier = params.main_name; | |||
| cf_plan.rules = params.compile_rules; | |||
| compile_files.push_back(std::move(cf_plan)); | |||
| if (sfile.kind == source_kind::test) { | |||
| test_sources.push_back(sfile); | |||
| } else if (sfile.kind == source_kind::app) { | |||
| app_sources.push_back(sfile); | |||
| } else { | |||
| lib_sources.push_back(sfile); | |||
| } | |||
| } | |||
| } | |||
| if (!app_sources.empty() || !test_sources.empty()) { | |||
| assert(false && "Apps/tests not implemented on this code path"); | |||
| } | |||
| if (!lib_sources.empty()) { | |||
| create_archive_plan ar_plan; | |||
| ar_plan.in_sources = lib_sources // | |||
| | ranges::views::transform([](auto&& sf) { return sf.path; }) // | |||
| | ranges::to_vector; | |||
| ar_plan.name = params.main_name; | |||
| ar_plan.out_dir = params.out_dir; | |||
| create_archives.push_back(std::move(ar_plan)); | |||
| } | |||
| return library_plan{compile_files, create_archives, link_executables, params.out_dir}; | |||
| } | |||
| namespace { | |||
| template <typename Range, typename Fn> | |||
| bool parallel_run(Range&& rng, int n_jobs, Fn&& fn) { | |||
| // We don't bother with a nice thread pool, as the overhead of most build | |||
| // tasks dwarf the cost of interlocking. | |||
| std::mutex mut; | |||
| auto iter = rng.begin(); | |||
| const auto stop = rng.end(); | |||
| std::vector<std::exception_ptr> exceptions; | |||
| auto run_one = [&]() mutable { | |||
| while (true) { | |||
| std::unique_lock lk{mut}; | |||
| if (!exceptions.empty()) { | |||
| break; | |||
| } | |||
| if (iter == stop) { | |||
| break; | |||
| } | |||
| auto&& item = *iter++; | |||
| lk.unlock(); | |||
| try { | |||
| fn(item); | |||
| } catch (...) { | |||
| lk.lock(); | |||
| exceptions.push_back(std::current_exception()); | |||
| break; | |||
| } | |||
| } | |||
| }; | |||
| std::unique_lock lk{mut}; | |||
| std::vector<std::thread> threads; | |||
| if (n_jobs < 1) { | |||
| n_jobs = std::thread::hardware_concurrency() + 2; | |||
| } | |||
| std::generate_n(std::back_inserter(threads), n_jobs, [&] { return std::thread(run_one); }); | |||
| lk.unlock(); | |||
| for (auto& t : threads) { | |||
| t.join(); | |||
| } | |||
| for (auto eptr : exceptions) { | |||
| try { | |||
| std::rethrow_exception(eptr); | |||
| } catch (const std::exception& e) { | |||
| spdlog::error(e.what()); | |||
| } | |||
| } | |||
| return exceptions.empty(); | |||
| } | |||
| } // namespace | |||
| void build_plan::compile_all(const toolchain& tc, int njobs, path_ref out_prefix) const { | |||
| std::vector<std::pair<fs::path, std::reference_wrapper<const compile_file_plan>>> comps; | |||
| for (const auto& lib : create_libraries) { | |||
| const auto lib_out_prefix = out_prefix / lib.out_subdir; | |||
| for (auto&& cf_plan : lib.compile_files) { | |||
| comps.emplace_back(lib_out_prefix, cf_plan); | |||
| } | |||
| } | |||
| auto okay = parallel_run(comps, njobs, [&](const auto& pair) { | |||
| const auto& [out_dir, cf_plan] = pair; | |||
| cf_plan.get().compile(tc, out_dir); | |||
| }); | |||
| if (!okay) { | |||
| throw std::runtime_error("Compilation failed."); | |||
| } | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| #pragma once | |||
| #include <dds/build/compile.hpp> | |||
| #include <dds/build/params.hpp> | |||
| #include <dds/build/sroot.hpp> | |||
| #include <dds/toolchain.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| namespace dds { | |||
| struct create_archive_plan { | |||
| std::vector<fs::path> in_sources; | |||
| std::string name; | |||
| fs::path out_dir; | |||
| }; | |||
| struct create_exe_plan { | |||
| std::vector<fs::path> in_sources; | |||
| std::string name; | |||
| fs::path out_dir; | |||
| }; | |||
| struct library_plan { | |||
| std::vector<compile_file_plan> compile_files; | |||
| std::vector<create_archive_plan> create_archives; | |||
| std::vector<create_exe_plan> link_executables; | |||
| fs::path out_subdir; | |||
| static library_plan create(const sroot& root, const sroot_build_params& params); | |||
| }; | |||
| struct build_plan { | |||
| std::vector<library_plan> create_libraries; | |||
| // static build_plan generate(const build_params& params); | |||
| void add_sroot(const sroot& root, const sroot_build_params& params); | |||
| void compile_all(const toolchain& tc, int njobs, path_ref out_prefix) const; | |||
| }; | |||
| } // namespace dds | |||
| @@ -0,0 +1,24 @@ | |||
| #include "./source_dir.hpp" | |||
| #include <range/v3/range/conversion.hpp> | |||
| #include <range/v3/view/filter.hpp> | |||
| #include <range/v3/view/transform.hpp> | |||
| using namespace dds; | |||
| std::vector<source_file> source_directory::sources() const { | |||
| using namespace ranges::views; | |||
| // Strips nullopt elements and lifts the value from the results | |||
| auto drop_nulls = // | |||
| filter([](auto&& opt) { return opt.has_value(); }) // | |||
| | transform([](auto&& opt) { return *opt; }); // | |||
| // Collect all source files from the directory | |||
| return // | |||
| fs::recursive_directory_iterator(path) // | |||
| | filter([](auto&& entry) { return entry.is_regular_file(); }) // | |||
| | transform([&](auto&& entry) { return source_file::from_path(entry, path); }) // | |||
| // source_file::from_path returns an optional. Drop nulls | |||
| | drop_nulls // | |||
| | ranges::to_vector; | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| #pragma once | |||
| #include <dds/source.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| #include <vector> | |||
| namespace dds { | |||
| struct source_directory { | |||
| fs::path path; | |||
| std::vector<source_file> sources() const; | |||
| bool exists() const noexcept { return fs::exists(path); } | |||
| }; | |||
| } // namespace dds | |||
| @@ -0,0 +1,24 @@ | |||
| #include "./sroot.hpp" | |||
| using namespace dds; | |||
| shared_compile_file_rules sroot::base_compile_rules() const noexcept { | |||
| auto inc_dir = include_dir(); | |||
| auto src_dir = this->src_dir(); | |||
| shared_compile_file_rules ret; | |||
| if (inc_dir.exists()) { | |||
| ret.include_dirs().push_back(inc_dir.path); | |||
| } | |||
| if (src_dir.exists()) { | |||
| ret.include_dirs().push_back(src_dir.path); | |||
| } | |||
| return ret; | |||
| } | |||
| fs::path sroot::public_include_dir() const noexcept { | |||
| auto inc_dir = include_dir(); | |||
| if (inc_dir.exists()) { | |||
| return inc_dir.path; | |||
| } | |||
| return src_dir().path; | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| #pragma once | |||
| #include <dds/build/compile.hpp> | |||
| #include <dds/build/source_dir.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| #include <vector> | |||
| namespace dds { | |||
| struct sroot { | |||
| fs::path path; | |||
| source_directory src_dir() const noexcept { return source_directory{path / "src"}; }; | |||
| source_directory include_dir() const noexcept { return source_directory{path / "include"}; } | |||
| fs::path public_include_dir() const noexcept; | |||
| shared_compile_file_rules base_compile_rules() const noexcept; | |||
| }; | |||
| struct sroot_build_params { | |||
| std::string main_name; | |||
| fs::path out_dir; | |||
| bool build_tests = false; | |||
| bool build_apps = false; | |||
| std::vector<fs::path> rt_link_libraries; | |||
| shared_compile_file_rules compile_rules; | |||
| }; | |||
| } // namespace dds | |||
| @@ -1,51 +0,0 @@ | |||
| #pragma once | |||
| #include <dds/source.hpp> | |||
| #include <dds/toolchain.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| #include <memory> | |||
| #include <optional> | |||
| #include <stdexcept> | |||
| namespace dds { | |||
| struct compile_failure : std::runtime_error { | |||
| using runtime_error::runtime_error; | |||
| }; | |||
| class compilation_rules { | |||
| struct rules_impl { | |||
| std::vector<fs::path> inc_dirs; | |||
| std::vector<std::string> defs; | |||
| fs::path base_path; | |||
| }; | |||
| std::shared_ptr<rules_impl> _impl = std::make_shared<rules_impl>(); | |||
| public: | |||
| compilation_rules() = default; | |||
| auto& base_path() noexcept { return _impl->base_path; } | |||
| const auto& base_path() const noexcept { return _impl->base_path; } | |||
| auto& include_dirs() noexcept { return _impl->inc_dirs; } | |||
| const auto& include_dirs() const noexcept { return _impl->inc_dirs; } | |||
| auto& defs() noexcept { return _impl->defs; } | |||
| const auto& defs() const noexcept { return _impl->defs; } | |||
| }; | |||
| struct file_compilation { | |||
| compilation_rules rules; | |||
| source_file source; | |||
| fs::path obj; | |||
| std::string owner_name; | |||
| bool enable_warnings = false; | |||
| void compile(const toolchain& tc) const; | |||
| }; | |||
| void execute_all(const std::vector<file_compilation>&, const toolchain& tc, int n_jobs); | |||
| } // namespace dds | |||
| @@ -1,4 +1,5 @@ | |||
| #include <dds/build.hpp> | |||
| #include <dds/build/plan.hpp> | |||
| #include <dds/logging.hpp> | |||
| #include <dds/repo/repo.hpp> | |||
| #include <dds/sdist.hpp> | |||
| @@ -359,19 +360,6 @@ struct cli_deps { | |||
| toolchain_flag tc_filepath{cmd}; | |||
| void _build_one_dep(const dds::sdist& dep) { | |||
| spdlog::info("Build dependency {} {}", | |||
| dep.manifest.name, | |||
| dep.manifest.version.to_string()); | |||
| dds::build_params params; | |||
| params.root = dep.path; | |||
| params.toolchain = tc_filepath.get_toolchain(); | |||
| params.out_root = build_dir.Get() | |||
| / fmt::format("{}-{}", dep.manifest.name, dep.manifest.version.to_string()); | |||
| dds::build(params, dep.manifest); | |||
| } | |||
| int run() { | |||
| auto man = parent.load_package_manifest(); | |||
| auto deps = dds::repository::with_repository( // | |||
| @@ -382,9 +370,9 @@ struct cli_deps { | |||
| man.dependencies.begin(), | |||
| man.dependencies.end()); | |||
| }); | |||
| for (auto&& dep : deps) { | |||
| _build_one_dep(dep); | |||
| } | |||
| auto plan = dds::create_deps_build_plan(deps); | |||
| plan.compile_all(tc_filepath.get_toolchain(), 6, build_dir.Get()); | |||
| return 0; | |||
| } | |||
| } build{*this}; | |||
| @@ -1,13 +1,16 @@ | |||
| #include "./deps.hpp" | |||
| #include <dds/sdist.hpp> | |||
| #include <dds/build/sroot.hpp> | |||
| #include <dds/repo/repo.hpp> | |||
| #include <dds/sdist.hpp> | |||
| #include <dds/util/string.hpp> | |||
| #include <range/v3/algorithm/partition_point.hpp> | |||
| #include <spdlog/fmt/fmt.h> | |||
| #include <range/v3/range/conversion.hpp> | |||
| #include <range/v3/view/transform.hpp> | |||
| #include <spdlog/spdlog.h> | |||
| #include <cctype> | |||
| #include <map> | |||
| using namespace dds; | |||
| @@ -51,7 +54,10 @@ void detail::do_find_deps(const repository& repo, const dependency& dep, std::ve | |||
| dep.version.to_string())); | |||
| } | |||
| sdist& new_sd = *sdist_opt; | |||
| auto insert_point = ranges::partition_point(sd, [&](const sdist& cand) { | |||
| for (const auto& inner_dep : new_sd.manifest.dependencies) { | |||
| do_find_deps(repo, inner_dep, sd); | |||
| } | |||
| auto insert_point = std::partition_point(sd.begin(), sd.end(), [&](const sdist& cand) { | |||
| return cand.path < new_sd.path; | |||
| }); | |||
| if (insert_point != sd.end() && insert_point->manifest.name == new_sd.manifest.name) { | |||
| @@ -62,4 +68,50 @@ void detail::do_find_deps(const repository& repo, const dependency& dep, std::ve | |||
| return; | |||
| } | |||
| sd.insert(insert_point, std::move(new_sd)); | |||
| } | |||
| } | |||
| using sdist_index_type = std::map<std::string, std::reference_wrapper<const sdist>>; | |||
| namespace { | |||
| void add_dep_includes(shared_compile_file_rules& rules, | |||
| const package_manifest& man, | |||
| const sdist_index_type& sd_idx) { | |||
| for (const dependency& dep : man.dependencies) { | |||
| auto found = sd_idx.find(dep.name); | |||
| if (found == sd_idx.end()) { | |||
| throw std::runtime_error( | |||
| fmt::format("Unable to resolve dependency '{}' (required by '{}')", | |||
| dep.name, | |||
| man.name)); | |||
| } | |||
| add_dep_includes(rules, found->second.get().manifest, sd_idx); | |||
| rules.include_dirs().push_back(sroot{found->second.get().path}.public_include_dir()); | |||
| } | |||
| } | |||
| void add_sdist_to_dep_plan(build_plan& plan, const sdist& sd, const sdist_index_type& sd_idx) { | |||
| auto root = dds::sroot{sd.path}; | |||
| shared_compile_file_rules comp_rules = root.base_compile_rules(); | |||
| add_dep_includes(comp_rules, sd.manifest, sd_idx); | |||
| sroot_build_params params; | |||
| params.main_name = sd.manifest.name; | |||
| params.compile_rules = comp_rules; | |||
| plan.add_sroot(root, params); | |||
| } | |||
| } // namespace | |||
| build_plan dds::create_deps_build_plan(const std::vector<sdist>& deps) { | |||
| auto sd_idx = deps | ranges::views::transform([](const auto& sd) { | |||
| return std::pair(sd.manifest.name, std::cref(sd)); | |||
| }) | |||
| | ranges::to<sdist_index_type>(); | |||
| build_plan plan; | |||
| for (const sdist& sd : deps) { | |||
| spdlog::info("Recording dependency: {}", sd.manifest.name); | |||
| add_sdist_to_dep_plan(plan, sd, sd_idx); | |||
| } | |||
| return plan; | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| #pragma once | |||
| #include <dds/build/plan.hpp> | |||
| #include <semver/version.hpp> | |||
| #include <string_view> | |||
| @@ -27,7 +29,7 @@ namespace detail { | |||
| void do_find_deps(const repository&, const dependency& dep, std::vector<sdist>& acc); | |||
| } // namespace detail | |||
| } // namespace detail | |||
| std::vector<sdist> find_dependencies(const repository& repo, const dependency& dep); | |||
| @@ -40,4 +42,6 @@ inline std::vector<sdist> find_dependencies(const repository& repo, Iter it, Snt | |||
| return acc; | |||
| } | |||
| build_plan create_deps_build_plan(const std::vector<sdist>& deps); | |||
| } // namespace dds | |||
| @@ -1,5 +1,6 @@ | |||
| #include <dds/library.hpp> | |||
| #include <dds/build/source_dir.hpp> | |||
| #include <dds/util/algo.hpp> | |||
| #include <spdlog/spdlog.h> | |||
| @@ -15,16 +16,16 @@ struct pf_info { | |||
| }; | |||
| pf_info collect_pf_sources(path_ref path) { | |||
| auto include_dir = path / "include"; | |||
| auto src_dir = path / "src"; | |||
| auto include_dir = source_directory{path / "include"}; | |||
| auto src_dir = source_directory{path / "src"}; | |||
| source_list sources; | |||
| if (fs::exists(include_dir)) { | |||
| if (!fs::is_directory(include_dir)) { | |||
| if (include_dir.exists()) { | |||
| if (!fs::is_directory(include_dir.path)) { | |||
| 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); | |||
| auto inc_sources = include_dir.sources(); | |||
| // Drop any source files we found within `include/` | |||
| erase_if(sources, [&](auto& info) { | |||
| if (info.kind != source_kind::header) { | |||
| @@ -37,15 +38,15 @@ pf_info collect_pf_sources(path_ref path) { | |||
| extend(sources, inc_sources); | |||
| } | |||
| if (fs::exists(src_dir)) { | |||
| if (!fs::is_directory(src_dir)) { | |||
| if (src_dir.exists()) { | |||
| if (!fs::is_directory(src_dir.path)) { | |||
| 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); | |||
| auto src_sources = src_dir.sources(); | |||
| extend(sources, src_sources); | |||
| } | |||
| return {std::move(sources), include_dir, src_dir}; | |||
| return {std::move(sources), include_dir.path, src_dir.path}; | |||
| } | |||
| } // namespace | |||
| @@ -21,10 +21,10 @@ struct sdist { | |||
| browns::md5::digest_type md5; | |||
| fs::path path; | |||
| sdist(package_manifest man, browns::md5::digest_type hash, path_ref path) | |||
| sdist(package_manifest man, browns::md5::digest_type hash, path_ref path_) | |||
| : manifest(std::move(man)) | |||
| , md5(hash) | |||
| , path(path) {} | |||
| , path(path_) {} | |||
| static sdist from_directory(path_ref p); | |||
| @@ -4,10 +4,6 @@ | |||
| #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> | |||
| @@ -58,28 +54,11 @@ std::optional<source_kind> dds::infer_source_kind(path_ref p) noexcept { | |||
| return source_kind::source; | |||
| } | |||
| std::optional<source_file> source_file::from_path(path_ref path) noexcept { | |||
| std::optional<source_file> source_file::from_path(path_ref path, path_ref base_path) noexcept { | |||
| auto kind = infer_source_kind(path); | |||
| if (!kind.has_value()) { | |||
| return std::nullopt; | |||
| } | |||
| return source_file{path, *kind}; | |||
| } | |||
| source_list source_file::collect_for_dir(path_ref src) { | |||
| using namespace ranges::views; | |||
| // Strips nullopt elements and lifts the value from the results | |||
| auto drop_nulls = // | |||
| filter([](auto&& opt) { return opt.has_value(); }) // | |||
| | transform([](auto&& opt) { return *opt; }); // | |||
| // Collect all source files from the directory | |||
| return // | |||
| fs::recursive_directory_iterator(src) // | |||
| | filter([](auto&& entry) { return entry.is_regular_file(); }) // | |||
| | transform([](auto&& entry) { return source_file::from_path(entry); }) // | |||
| // source_file::from_path returns an optional. Drop nulls | |||
| | drop_nulls // | |||
| | ranges::to_vector; | |||
| return source_file{path, base_path, *kind}; | |||
| } | |||
| @@ -18,10 +18,10 @@ std::optional<source_kind> infer_source_kind(path_ref) noexcept; | |||
| struct source_file { | |||
| fs::path path; | |||
| fs::path basis_path; | |||
| source_kind kind; | |||
| static std::optional<source_file> from_path(path_ref) noexcept; | |||
| static std::vector<source_file> collect_for_dir(path_ref); | |||
| static std::optional<source_file> from_path(path_ref path, path_ref base_path) noexcept; | |||
| }; | |||
| using source_list = std::vector<source_file>; | |||