#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 |