@@ -573,6 +573,113 @@ struct cli_sdist { | |||
} | |||
}; | |||
void load_project_deps(dds::builder& bd, | |||
const dds::package_manifest& man, | |||
dds::path_ref cat_path, | |||
dds::path_ref repo_path) { | |||
auto cat = dds::catalog::open(cat_path); | |||
// Build the dependencies | |||
dds::repository::with_repository( // | |||
repo_path, | |||
dds::repo_flags::write_lock | dds::repo_flags::create_if_absent, | |||
[&](dds::repository repo) { | |||
// Download dependencies | |||
auto deps = repo.solve(man.dependencies, cat); | |||
dds::get_all(deps, repo, cat); | |||
for (const dds::package_id& pk : deps) { | |||
auto sdist_ptr = repo.find(pk); | |||
assert(sdist_ptr); | |||
dds::sdist_build_params deps_params; | |||
deps_params.subdir | |||
= dds::fs::path("_deps") / sdist_ptr->manifest.pkg_id.to_string(); | |||
bd.add(*sdist_ptr, deps_params); | |||
} | |||
}); | |||
} | |||
dds::builder create_project_builder(dds::path_ref pr_dir, | |||
dds::path_ref cat_path, | |||
dds::path_ref repo_path, | |||
bool load_deps, | |||
const dds::sdist_build_params& project_params) { | |||
auto man = dds::package_manifest::load_from_directory(pr_dir).value_or(dds::package_manifest{}); | |||
dds::builder builder; | |||
if (load_deps) { | |||
load_project_deps(builder, man, cat_path, repo_path); | |||
} | |||
builder.add(dds::sdist{std::move(man), pr_dir}, project_params); | |||
return builder; | |||
} | |||
/* | |||
###### ####### ## ## ######## #### ## ######## | |||
## ## ## ## ### ### ## ## ## ## ## | |||
## ## ## #### #### ## ## ## ## ## | |||
## ## ## ## ### ## ######## ## ## ###### | |||
## ## ## ## ## ## ## ## ## | |||
## ## ## ## ## ## ## ## ## ## | |||
###### ####### ## ## ## #### ######## ######## | |||
*/ | |||
struct cli_compile_file { | |||
cli_base& base; | |||
args::Command cmd{base.cmd_group, "compile-file", "Compile a single file"}; | |||
common_flags _flags{cmd}; | |||
common_project_flags project{cmd}; | |||
catalog_path_flag cat_path{cmd}; | |||
repo_path_flag repo_path{cmd}; | |||
args::Flag no_warnings{cmd, "no-warnings", "Disable compiler warnings", {"no-warnings"}}; | |||
toolchain_flag tc_filepath{cmd}; | |||
path_flag | |||
lm_index{cmd, | |||
"lm_index", | |||
"Path to an existing libman index from which to load deps (usually INDEX.lmi)", | |||
{"lm-index", 'I'}}; | |||
num_jobs_flag n_jobs{cmd}; | |||
path_flag out{cmd, | |||
"out", | |||
"The root build directory", | |||
{"out"}, | |||
dds::fs::current_path() / "_build"}; | |||
args::PositionalList<dds::fs::path> source_files{cmd, | |||
"source-files", | |||
"One or more source files to compile"}; | |||
int run() { | |||
dds::sdist_build_params main_params = { | |||
.subdir = "", | |||
.build_tests = true, | |||
.build_apps = true, | |||
.enable_warnings = !no_warnings.Get(), | |||
}; | |||
auto bd = create_project_builder(project.root.Get(), | |||
cat_path.Get(), | |||
repo_path.Get(), | |||
/* load_deps = */ !lm_index, | |||
main_params); | |||
bd.compile_files(source_files.Get(), | |||
{ | |||
.out_root = out.Get(), | |||
.existing_lm_index | |||
= lm_index ? std::make_optional(lm_index.Get()) : std::nullopt, | |||
.emit_lmi = {}, | |||
.toolchain = tc_filepath.get_toolchain(), | |||
.parallel_jobs = n_jobs.Get(), | |||
}); | |||
return 0; | |||
} | |||
}; | |||
/* | |||
######## ## ## #### ## ######## | |||
## ## ## ## ## ## ## ## | |||
@@ -614,45 +721,26 @@ struct cli_build { | |||
dds::fs::current_path() / "_build"}; | |||
int run() { | |||
dds::build_params params; | |||
params.out_root = out.Get(); | |||
params.toolchain = tc_filepath.get_toolchain(); | |||
params.parallel_jobs = n_jobs.Get(); | |||
auto man = dds::package_manifest::load_from_directory(project.root.Get()) | |||
.value_or(dds::package_manifest{}); | |||
dds::builder bd; | |||
dds::sdist_build_params main_params; | |||
main_params.build_apps = !no_apps.Get(); | |||
main_params.enable_warnings = !no_warnings.Get(); | |||
main_params.run_tests = main_params.build_tests = !no_tests.Get(); | |||
bd.add(dds::sdist{man, project.root.Get()}, main_params); | |||
if (lm_index) { | |||
params.existing_lm_index = lm_index.Get(); | |||
} else { | |||
// Download and build dependencies | |||
// Build the dependencies | |||
auto cat = cat_path.open(); | |||
dds::repository::with_repository( // | |||
this->repo_path.Get(), | |||
dds::repo_flags::write_lock | dds::repo_flags::create_if_absent, | |||
[&](dds::repository repo) { | |||
// Download dependencies | |||
auto deps = repo.solve(man.dependencies, cat); | |||
dds::get_all(deps, repo, cat); | |||
for (const dds::package_id& pk : deps) { | |||
auto sdist_ptr = repo.find(pk); | |||
assert(sdist_ptr); | |||
dds::sdist_build_params deps_params; | |||
deps_params.subdir | |||
= dds::fs::path("_deps") / sdist_ptr->manifest.pkg_id.to_string(); | |||
bd.add(*sdist_ptr, deps_params); | |||
} | |||
}); | |||
} | |||
bd.build(params); | |||
dds::sdist_build_params main_params = { | |||
.subdir = "", | |||
.build_tests = !no_tests.Get(), | |||
.run_tests = !no_tests.Get(), | |||
.build_apps = !no_apps.Get(), | |||
.enable_warnings = !no_warnings.Get(), | |||
}; | |||
auto bd = create_project_builder(project.root.Get(), | |||
cat_path.Get(), | |||
repo_path.Get(), | |||
/* load_deps = */ !lm_index, | |||
main_params); | |||
bd.build({ | |||
.out_root = out.Get(), | |||
.existing_lm_index = lm_index ? std::make_optional(lm_index.Get()) : std::nullopt, | |||
.emit_lmi = {}, | |||
.toolchain = tc_filepath.get_toolchain(), | |||
.parallel_jobs = n_jobs.Get(), | |||
}); | |||
return 0; | |||
} | |||
}; | |||
@@ -760,12 +848,13 @@ int main(int argc, char** argv) { | |||
spdlog::set_pattern("[%H:%M:%S] [%^%-5l%$] %v"); | |||
args::ArgumentParser parser("DDS - The drop-dead-simple library manager"); | |||
cli_base cli{parser}; | |||
cli_build build{cli}; | |||
cli_sdist sdist{cli}; | |||
cli_repo repo{cli}; | |||
cli_catalog catalog{cli}; | |||
cli_build_deps build_deps{cli}; | |||
cli_base cli{parser}; | |||
cli_compile_file compile_file{cli}; | |||
cli_build build{cli}; | |||
cli_sdist sdist{cli}; | |||
cli_repo repo{cli}; | |||
cli_catalog catalog{cli}; | |||
cli_build_deps build_deps{cli}; | |||
try { | |||
parser.ParseCLI(argc, argv); | |||
@@ -785,6 +874,8 @@ int main(int argc, char** argv) { | |||
if (cli._verify_ident) { | |||
std::cout << "yes\n"; | |||
return 0; | |||
} else if (compile_file.cmd) { | |||
return compile_file.run(); | |||
} else if (build.cmd) { | |||
return build.run(); | |||
} else if (sdist.cmd) { |
@@ -205,14 +205,15 @@ void write_lmi(build_env_ref env, const build_plan& plan, path_ref base_dir, pat | |||
} | |||
} | |||
} // namespace | |||
void builder::build(const build_params& params) const { | |||
template <typename Func> | |||
void with_build_plan(const build_params& params, | |||
const std::vector<sdist_target>& sdists, | |||
Func&& fn) { | |||
fs::create_directories(params.out_root); | |||
auto db = database::open(params.out_root / ".dds.db"); | |||
state st; | |||
auto plan = prepare_build_plan(st, _sdists); | |||
auto plan = prepare_build_plan(st, sdists); | |||
auto ureqs = prepare_ureqs(plan, params.toolchain, params.out_root); | |||
build_env env{ | |||
params.toolchain, | |||
@@ -239,30 +240,44 @@ void builder::build(const build_params& params) const { | |||
plan.render_all(env); | |||
dds::stopwatch sw; | |||
plan.compile_all(env, params.parallel_jobs); | |||
dds_log(info, "Compilation completed in {:L}ms", sw.elapsed_ms().count()); | |||
fn(std::move(env), std::move(plan)); | |||
} | |||
sw.reset(); | |||
plan.archive_all(env, params.parallel_jobs); | |||
dds_log(info, "Archiving completed in {:L}ms", sw.elapsed_ms().count()); | |||
} // namespace | |||
sw.reset(); | |||
plan.link_all(env, params.parallel_jobs); | |||
dds_log(info, "Runtime binary linking completed in {:L}ms", sw.elapsed_ms().count()); | |||
void builder::compile_files(const std::vector<fs::path>& files, const build_params& params) const { | |||
with_build_plan(params, _sdists, [&](build_env_ref env, build_plan plan) { | |||
plan.compile_files(env, params.parallel_jobs, files); | |||
}); | |||
} | |||
sw.reset(); | |||
auto test_failures = plan.run_all_tests(env, params.parallel_jobs); | |||
dds_log(info, "Test execution finished in {:L}ms", sw.elapsed_ms().count()); | |||
void builder::build(const build_params& params) const { | |||
with_build_plan(params, _sdists, [&](build_env_ref env, const build_plan& plan) { | |||
dds::stopwatch sw; | |||
plan.compile_all(env, params.parallel_jobs); | |||
dds_log(info, "Compilation completed in {:L}ms", sw.elapsed_ms().count()); | |||
for (auto& fail : test_failures) { | |||
log_failure(fail); | |||
} | |||
if (!test_failures.empty()) { | |||
throw_user_error<errc::test_failure>(); | |||
} | |||
sw.reset(); | |||
plan.archive_all(env, params.parallel_jobs); | |||
dds_log(info, "Archiving completed in {:L}ms", sw.elapsed_ms().count()); | |||
if (params.emit_lmi) { | |||
write_lmi(env, plan, params.out_root, *params.emit_lmi); | |||
} | |||
sw.reset(); | |||
plan.link_all(env, params.parallel_jobs); | |||
dds_log(info, "Runtime binary linking completed in {:L}ms", sw.elapsed_ms().count()); | |||
sw.reset(); | |||
auto test_failures = plan.run_all_tests(env, params.parallel_jobs); | |||
dds_log(info, "Test execution finished in {:L}ms", sw.elapsed_ms().count()); | |||
for (auto& fail : test_failures) { | |||
log_failure(fail); | |||
} | |||
if (!test_failures.empty()) { | |||
throw_user_error<errc::test_failure>(); | |||
} | |||
if (params.emit_lmi) { | |||
write_lmi(env, plan, params.out_root, *params.emit_lmi); | |||
} | |||
}); | |||
} |
@@ -53,6 +53,11 @@ public: | |||
* Execute the build | |||
*/ | |||
void build(const build_params& params) const; | |||
/** | |||
* Compile one or more source files | |||
*/ | |||
void compile_files(const std::vector<fs::path>& files, const build_params& params) const; | |||
}; | |||
} // namespace dds |
@@ -3,8 +3,10 @@ | |||
#include <dds/build/iter_compilations.hpp> | |||
#include <dds/build/plan/compile_exec.hpp> | |||
#include <dds/error/errors.hpp> | |||
#include <dds/util/log.hpp> | |||
#include <dds/util/parallel.hpp> | |||
#include <range/v3/algorithm/any_of.hpp> | |||
#include <range/v3/range/conversion.hpp> | |||
#include <range/v3/view/concat.hpp> | |||
#include <range/v3/view/filter.hpp> | |||
@@ -46,6 +48,52 @@ void build_plan::compile_all(const build_env& env, int njobs) const { | |||
} | |||
} | |||
void build_plan::compile_files(const build_env& env, | |||
int njobs, | |||
const std::vector<fs::path>& filepaths) const { | |||
struct pending_file { | |||
bool marked = false; | |||
fs::path filepath; | |||
}; | |||
auto as_pending = // | |||
ranges::view::all(filepaths) // | |||
| ranges::view::transform([](auto&& path) { | |||
return pending_file{false, fs::weakly_canonical(path)}; | |||
}) | |||
| ranges::to_vector; | |||
auto check_compilation = [&](const compile_file_plan& comp) { | |||
return ranges::any_of(as_pending, [&](pending_file& f) { | |||
bool same_file = f.filepath == fs::weakly_canonical(comp.source_path()); | |||
if (same_file) { | |||
f.marked = true; | |||
} | |||
return same_file; | |||
}); | |||
}; | |||
auto comps | |||
= iter_compilations(*this) | ranges::view::filter(check_compilation) | ranges::to_vector; | |||
bool any_unmarked = false; | |||
auto unmarked = ranges::view::filter(as_pending, ranges::not_fn(&pending_file::marked)); | |||
for (auto&& um : unmarked) { | |||
dds_log(error, "Source file [{}] is not compiled by this project", um.filepath.string()); | |||
any_unmarked = true; | |||
} | |||
if (any_unmarked) { | |||
throw_user_error<errc::compile_failure>( | |||
"One or more requested files is not part of this project (See above)"); | |||
} | |||
auto okay = dds::compile_all(comps, env, njobs); | |||
if (!okay) { | |||
throw_user_error<errc::compile_failure>(); | |||
} | |||
} | |||
void build_plan::archive_all(const build_env& env, int njobs) const { | |||
auto okay = parallel_run(iter_libraries(*this), njobs, [&](const library_plan& lib) { | |||
if (lib.archive_plan()) { |
@@ -45,6 +45,11 @@ public: | |||
*/ | |||
void link_all(const build_env& env, int njobs) const; | |||
/** | |||
* Compile the files given in the vector of file paths. | |||
*/ | |||
void compile_files(const build_env& env, int njobs, const std::vector<fs::path>& paths) const; | |||
/** | |||
* Execute all tests defined in the plan. Returns information for every failed test. | |||
*/ |