| #include <dds/proc.hpp> | #include <dds/proc.hpp> | ||||
| #include <dds/source.hpp> | #include <dds/source.hpp> | ||||
| #include <dds/toolchain.hpp> | #include <dds/toolchain.hpp> | ||||
| #include <dds/compdb.hpp> | |||||
| #include <dds/usage_reqs.hpp> | #include <dds/usage_reqs.hpp> | ||||
| #include <dds/util/algo.hpp> | #include <dds/util/algo.hpp> | ||||
| #include <dds/util/string.hpp> | #include <dds/util/string.hpp> | ||||
| } | } | ||||
| dds::build_env env{params.toolchain, params.out_root}; | dds::build_env env{params.toolchain, params.out_root}; | ||||
| if (params.generate_compdb) { | |||||
| generate_compdb(plan, env); | |||||
| } | |||||
| plan.compile_all(env, params.parallel_jobs); | plan.compile_all(env, params.parallel_jobs); | ||||
| plan.archive_all(env, params.parallel_jobs); | plan.archive_all(env, params.parallel_jobs); | ||||
| plan.link_all(env, params.parallel_jobs); | plan.link_all(env, params.parallel_jobs); |
| #pragma once | |||||
| #include <dds/build/plan.hpp> | |||||
| #include <range/v3/view/concat.hpp> | |||||
| #include <range/v3/view/filter.hpp> | |||||
| #include <range/v3/view/join.hpp> | |||||
| #include <range/v3/view/transform.hpp> | |||||
| namespace dds { | |||||
| inline auto iter_libraries(const build_plan& plan) { | |||||
| return // | |||||
| plan.packages() // | |||||
| | ranges::views::transform(&package_plan::libraries) // | |||||
| | ranges::views::join // | |||||
| ; | |||||
| } | |||||
| inline auto iter_compilations(const build_plan& plan) { | |||||
| auto lib_compiles = // | |||||
| iter_libraries(plan) // | |||||
| | ranges::views::transform(&library_plan::create_archive) // | |||||
| | ranges::views::filter([&](auto&& opt) { return bool(opt); }) // | |||||
| | ranges::views::transform([&](auto&& opt) -> auto& { return opt->compile_files(); }) // | |||||
| | ranges::views::join // | |||||
| ; | |||||
| auto exe_compiles = // | |||||
| iter_libraries(plan) // | |||||
| | ranges::views::transform(&library_plan::executables) // | |||||
| | ranges::views::join // | |||||
| | ranges::views::transform(&link_executable_plan::main_compile_file) // | |||||
| ; | |||||
| return ranges::views::concat(lib_compiles, exe_compiles); | |||||
| } | |||||
| } // namespace dds |
| bool enable_warnings = false; | bool enable_warnings = false; | ||||
| bool build_apps = false; | bool build_apps = false; | ||||
| bool build_deps = false; | bool build_deps = false; | ||||
| bool generate_compdb = true; | |||||
| int parallel_jobs = 0; | int parallel_jobs = 0; | ||||
| }; | }; | ||||
| #include "./plan.hpp" | #include "./plan.hpp" | ||||
| #include <dds/build/iter_compilations.hpp> | |||||
| #include <dds/proc.hpp> | #include <dds/proc.hpp> | ||||
| #include <range/v3/action/join.hpp> | #include <range/v3/action/join.hpp> | ||||
| } // namespace | } // namespace | ||||
| namespace { | |||||
| auto all_libraries(const build_plan& plan) { | |||||
| return // | |||||
| plan.packages() // | |||||
| | ranges::views::transform(&package_plan::libraries) // | |||||
| | ranges::views::join // | |||||
| ; | |||||
| } | |||||
| } // namespace | |||||
| void build_plan::compile_all(const build_env& env, int njobs) const { | void build_plan::compile_all(const build_env& env, int njobs) const { | ||||
| auto lib_compiles = // | |||||
| all_libraries(*this) // | |||||
| | ranges::views::transform(&library_plan::create_archive) // | |||||
| | ranges::views::filter([&](auto&& opt) { return bool(opt); }) // | |||||
| | ranges::views::transform([&](auto&& opt) -> auto& { return opt->compile_files(); }) // | |||||
| | ranges::views::join // | |||||
| ; | |||||
| auto exe_compiles = // | |||||
| all_libraries(*this) // | |||||
| | ranges::views::transform(&library_plan::executables) // | |||||
| | ranges::views::join // | |||||
| | ranges::views::transform(&link_executable_plan::main_compile_file) // | |||||
| ; | |||||
| auto all_compiles = ranges::views::concat(lib_compiles, exe_compiles); | |||||
| auto okay | |||||
| = parallel_run(all_compiles, njobs, [&](const compile_file_plan& cf) { cf.compile(env); }); | |||||
| auto okay = parallel_run(iter_compilations(*this), njobs, [&](const compile_file_plan& cf) { | |||||
| cf.compile(env); | |||||
| }); | |||||
| if (!okay) { | if (!okay) { | ||||
| throw std::runtime_error("Compilation failed."); | throw std::runtime_error("Compilation failed."); | ||||
| } | } | ||||
| } | } | ||||
| void build_plan::archive_all(const build_env& env, int njobs) const { | void build_plan::archive_all(const build_env& env, int njobs) const { | ||||
| parallel_run(all_libraries(*this), njobs, [&](const library_plan& lib) { | |||||
| parallel_run(iter_libraries(*this), njobs, [&](const library_plan& lib) { | |||||
| if (lib.create_archive()) { | if (lib.create_archive()) { | ||||
| lib.create_archive()->archive(env); | lib.create_archive()->archive(env); | ||||
| } | } | ||||
| } | } | ||||
| void build_plan::link_all(const build_env& env, int) const { | void build_plan::link_all(const build_env& env, int) const { | ||||
| for (auto&& lib : all_libraries(*this)) { | |||||
| for (auto&& lib : iter_libraries(*this)) { | |||||
| for (auto&& exe : lib.executables()) { | for (auto&& exe : lib.executables()) { | ||||
| exe.link(env, lib); | exe.link(env, lib); | ||||
| } | } |
| using namespace dds; | using namespace dds; | ||||
| std::vector<std::string> compile_file_plan::generate_compile_command(build_env_ref env) const | |||||
| noexcept { | |||||
| compile_file_spec spec{_source.path, calc_object_file_path(env)}; | |||||
| spec.enable_warnings = _rules.enable_warnings(); | |||||
| extend(spec.include_dirs, _rules.include_dirs()); | |||||
| extend(spec.definitions, _rules.defs()); | |||||
| return env.toolchain.create_compile_command(spec); | |||||
| } | |||||
| void compile_file_plan::compile(const build_env& env) const { | void compile_file_plan::compile(const build_env& env) const { | ||||
| const auto obj_path = calc_object_file_path(env); | const auto obj_path = calc_object_file_path(env); | ||||
| fs::create_directories(obj_path.parent_path()); | fs::create_directories(obj_path.parent_path()); | ||||
| fs::relative(_source.path, _source.basis_path).string()); | fs::relative(_source.path, _source.basis_path).string()); | ||||
| auto start_time = std::chrono::steady_clock::now(); | auto start_time = std::chrono::steady_clock::now(); | ||||
| 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()); | |||||
| auto cmd = env.toolchain.create_compile_command(spec); | |||||
| auto cmd = generate_compile_command(env); | |||||
| auto compile_res = run_proc(cmd); | auto compile_res = run_proc(cmd); | ||||
| auto end_time = std::chrono::steady_clock::now(); | auto end_time = std::chrono::steady_clock::now(); | ||||
| } | } | ||||
| // 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. | ||||
| if (compile_res.output.find(spec.source_path.filename().string() + "\r\n") == 0) { | |||||
| compile_res.output.erase(0, spec.source_path.filename().string().length() + 2); | |||||
| if (compile_res.output.find(_source.path.filename().string() + "\r\n") == 0) { | |||||
| compile_res.output.erase(0, _source.path.filename().string().length() + 2); | |||||
| } | } | ||||
| if (!compile_res.output.empty()) { | if (!compile_res.output.empty()) { | ||||
| spdlog::warn("While compiling file {} [{}]:\n{}", | spdlog::warn("While compiling file {} [{}]:\n{}", | ||||
| spec.source_path.string(), | |||||
| _source.path.string(), | |||||
| quote_command(cmd), | quote_command(cmd), | ||||
| compile_res.output); | compile_res.output); | ||||
| } | } |
| , _qualifier(qual) | , _qualifier(qual) | ||||
| , _subdir(subdir) {} | , _subdir(subdir) {} | ||||
| std::vector<std::string> generate_compile_command(build_env_ref) const noexcept; | |||||
| const source_file& source() const noexcept { return _source; } | const source_file& source() const noexcept { return _source; } | ||||
| path_ref source_path() const noexcept { return _source.path; } | path_ref source_path() const noexcept { return _source.path; } | ||||
| #include "./compdb.hpp" | |||||
| #include <dds/build/iter_compilations.hpp> | |||||
| #include <dds/proc.hpp> | |||||
| #include <dds/util/fs.hpp> | |||||
| #include <nlohmann/json.hpp> | |||||
| #include <range/v3/view/join.hpp> | |||||
| #include <range/v3/view/transform.hpp> | |||||
| using namespace dds; | |||||
| void dds::generate_compdb(const build_plan& plan, build_env_ref env) { | |||||
| auto compdb = nlohmann::json::array(); | |||||
| for (const compile_file_plan& cf : iter_compilations(plan)) { | |||||
| auto command = cf.generate_compile_command(env); | |||||
| auto entry = nlohmann::json::object({ | |||||
| {"directory", env.output_root}, | |||||
| {"arguments", command}, | |||||
| {"file", cf.source_path().string()}, | |||||
| }); | |||||
| compdb.push_back(std::move(entry)); | |||||
| } | |||||
| fs::create_directories(env.output_root); | |||||
| auto compdb_file = env.output_root / "compile_commands.json"; | |||||
| auto ostream = open(compdb_file, std::ios::binary | std::ios::out); | |||||
| ostream << compdb.dump(2); | |||||
| } |
| #pragma once | |||||
| #include <dds/build/plan.hpp> | |||||
| namespace dds { | |||||
| void generate_compdb(const build_plan&, build_env_ref); | |||||
| } // namespace dds |