#include "../options.hpp" | |||||
#include "./build_common.hpp" | |||||
#include <dds/build/builder.hpp> | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/error/errors.hpp> | |||||
#include <dds/remote/remote.hpp> | |||||
#include <dds/toolchain/from_json.hpp> | |||||
using namespace dds; | |||||
namespace dds::cli::cmd { | |||||
int build(const options& opts) { | |||||
if (!opts.build.add_repos.empty()) { | |||||
auto cat = opts.open_catalog(); | |||||
for (auto& str : opts.build.add_repos) { | |||||
auto repo = remote_repository::connect(str); | |||||
repo.store(cat.database()); | |||||
} | |||||
} | |||||
if (opts.build.update_repos || !opts.build.add_repos.empty()) { | |||||
update_all_remotes(opts.open_catalog().database()); | |||||
} | |||||
auto builder = create_project_builder(opts); | |||||
builder.build({ | |||||
.out_root = opts.out_path.value_or(fs::current_path() / "_build"), | |||||
.existing_lm_index = opts.build.lm_index, | |||||
.emit_lmi = {}, | |||||
.toolchain = opts.load_toolchain(), | |||||
.parallel_jobs = opts.jobs, | |||||
}); | |||||
return 0; | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "./build_common.hpp" | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/catalog/get.hpp> | |||||
#include <dds/repo/repo.hpp> | |||||
using namespace dds; | |||||
builder dds::cli::create_project_builder(const dds::cli::options& opts) { | |||||
sdist_build_params main_params = { | |||||
.subdir = "", | |||||
.build_tests = opts.build.want_tests, | |||||
.run_tests = opts.build.want_tests, | |||||
.build_apps = opts.build.want_apps, | |||||
.enable_warnings = !opts.disable_warnings, | |||||
}; | |||||
auto man = package_manifest::load_from_directory(opts.project_dir).value_or(package_manifest{}); | |||||
auto cat_path = opts.pkg_db_dir.value_or(catalog::default_path()); | |||||
auto repo_path = opts.pkg_cache_dir.value_or(repository::default_local_path()); | |||||
builder builder; | |||||
if (!opts.build.lm_index.has_value()) { | |||||
auto cat = catalog::open(cat_path); | |||||
// Build the dependencies | |||||
repository::with_repository( // | |||||
repo_path, | |||||
repo_flags::write_lock | repo_flags::create_if_absent, | |||||
[&](repository repo) { | |||||
// Download dependencies | |||||
auto deps = repo.solve(man.dependencies, cat); | |||||
get_all(deps, repo, cat); | |||||
for (const package_id& pk : deps) { | |||||
auto sdist_ptr = repo.find(pk); | |||||
assert(sdist_ptr); | |||||
sdist_build_params deps_params; | |||||
deps_params.subdir = fs::path("_deps") / sdist_ptr->manifest.pkg_id.to_string(); | |||||
builder.add(*sdist_ptr, deps_params); | |||||
} | |||||
}); | |||||
} | |||||
builder.add(sdist{std::move(man), opts.project_dir}, main_params); | |||||
return builder; | |||||
} |
#include "../options.hpp" | |||||
#include <dds/build/builder.hpp> | |||||
#include <functional> | |||||
namespace dds::cli { | |||||
dds::builder create_project_builder(const options& opts); | |||||
} // namespace dds::cli |
#include "../options.hpp" | |||||
#include <dds/build/builder.hpp> | |||||
#include <dds/build/params.hpp> | |||||
#include <dds/catalog/get.hpp> | |||||
#include <dds/repo/repo.hpp> | |||||
#include <range/v3/action/join.hpp> | |||||
#include <range/v3/range/conversion.hpp> | |||||
#include <range/v3/view/concat.hpp> | |||||
#include <range/v3/view/transform.hpp> | |||||
namespace dds::cli::cmd { | |||||
int build_deps(const options& opts) { | |||||
dds::build_params params{ | |||||
.out_root = opts.out_path.value_or(fs::current_path() / "_deps"), | |||||
.existing_lm_index = {}, | |||||
.emit_lmi = opts.build.lm_index.value_or("INDEX.lmi"), | |||||
.toolchain = opts.load_toolchain(), | |||||
.parallel_jobs = opts.jobs, | |||||
}; | |||||
dds::builder bd; | |||||
dds::sdist_build_params sdist_params; | |||||
auto all_file_deps = opts.build_deps.deps_files // | |||||
| ranges::views::transform([&](auto dep_fpath) { | |||||
dds_log(info, "Reading deps from {}", dep_fpath.string()); | |||||
return dds::dependency_manifest::from_file(dep_fpath).dependencies; | |||||
}) | |||||
| ranges::actions::join; | |||||
auto cmd_deps = ranges::views::transform(opts.build_deps.deps, [&](auto dep_str) { | |||||
return dds::dependency::parse_depends_string(dep_str); | |||||
}); | |||||
auto all_deps = ranges::views::concat(all_file_deps, cmd_deps) | ranges::to_vector; | |||||
auto cat = opts.open_catalog(); | |||||
dds::repository::with_repository( // | |||||
opts.pkg_cache_dir.value_or(repository::default_local_path()), | |||||
dds::repo_flags::write_lock | dds::repo_flags::create_if_absent, | |||||
[&](dds::repository repo) { | |||||
// Download dependencies | |||||
dds_log(info, "Loading {} dependencies", all_deps.size()); | |||||
auto deps = repo.solve(all_deps, 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 = sdist_ptr->manifest.pkg_id.to_string(); | |||||
dds_log(info, "Dependency: {}", sdist_ptr->manifest.pkg_id.to_string()); | |||||
bd.add(*sdist_ptr, deps_params); | |||||
} | |||||
}); | |||||
bd.build(params); | |||||
return 0; | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include "./build_common.hpp" | |||||
namespace dds::cli::cmd { | |||||
int compile_file(const options& opts) { | |||||
auto builder = create_project_builder(opts); | |||||
builder.compile_files(opts.compile_file.files, | |||||
{ | |||||
.out_root = opts.out_path.value_or(fs::current_path() / "_build"), | |||||
.existing_lm_index = opts.build.lm_index, | |||||
.emit_lmi = {}, | |||||
.toolchain = opts.load_toolchain(), | |||||
.parallel_jobs = opts.jobs, | |||||
}); | |||||
return 0; | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/catalog/get.hpp> | |||||
#include <dds/dym.hpp> | |||||
#include <dds/error/errors.hpp> | |||||
#include <dds/http/session.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <json5/parse_data.hpp> | |||||
namespace dds::cli::cmd { | |||||
static int _pkg_get(const options& opts) { | |||||
auto cat = opts.open_catalog(); | |||||
for (const auto& item : opts.pkg.get.pkgs) { | |||||
auto id = package_id::parse(item); | |||||
dds::dym_target dym; | |||||
auto info = cat.get(id); | |||||
if (!info) { | |||||
dds::throw_user_error<dds::errc::no_such_catalog_package>( | |||||
"No package in the catalog matched the ID '{}'.{}", item, dym.sentence_suffix()); | |||||
} | |||||
auto tsd = get_package_sdist(*info); | |||||
auto dest = opts.out_path.value_or(fs::current_path()) / id.to_string(); | |||||
dds_log(info, "Create sdist at {}", dest.string()); | |||||
fs::remove_all(dest); | |||||
safe_rename(tsd.sdist.path, dest); | |||||
} | |||||
return 0; | |||||
} | |||||
int pkg_get(const options& opts) { | |||||
return boost::leaf::try_catch( // | |||||
[&] { | |||||
try { | |||||
return _pkg_get(opts); | |||||
} catch (...) { | |||||
dds::capture_exception(); | |||||
} | |||||
}, | |||||
[&](neo::url_validation_error url_err, dds::e_url_string bad_url) { | |||||
dds_log(error, | |||||
"Invalid package URL in the database [{}]: {}", | |||||
bad_url.value, | |||||
url_err.what()); | |||||
return 1; | |||||
}, | |||||
[&](const json5::parse_error& e, dds::e_http_url bad_url) { | |||||
dds_log(error, | |||||
"Error parsing JSON5 document package downloaded from [{}]: {}", | |||||
bad_url.value, | |||||
e.what()); | |||||
return 1; | |||||
}, | |||||
[](dds::e_sqlite3_error_exc e) { | |||||
dds_log(error, "Error accessing the package database: {}", e.message); | |||||
return 1; | |||||
}, | |||||
[&](dds::e_system_error_exc e, dds::e_http_connect conn) { | |||||
dds_log(error, | |||||
"Error opening connection to [{}:{}]: {}", | |||||
conn.host, | |||||
conn.port, | |||||
e.message); | |||||
return 1; | |||||
}); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include <dds/http/session.hpp> | |||||
#include <dds/repo/repo.hpp> | |||||
#include <dds/source/dist.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <json5/parse_data.hpp> | |||||
#include <neo/assert.hpp> | |||||
#include <neo/url/parse.hpp> | |||||
#include <iostream> | |||||
#include <string_view> | |||||
namespace dds::cli::cmd { | |||||
static int _pkg_import(const options& opts) { | |||||
return repository::with_repository( // | |||||
opts.pkg_cache_dir.value_or(repository::default_local_path()), | |||||
repo_flags::write_lock | repo_flags::create_if_absent, | |||||
[&](auto repo) { | |||||
for (std::string_view tgz_where : opts.pkg.import.items) { | |||||
neo_assertion_breadcrumbs("Importing sdist", tgz_where); | |||||
auto tmp_sd | |||||
= (tgz_where.starts_with("http://") || tgz_where.starts_with("https://")) | |||||
? download_expand_sdist_targz(tgz_where) | |||||
: expand_sdist_targz(tgz_where); | |||||
neo_assertion_breadcrumbs("Importing from temporary directory", | |||||
tmp_sd.tmpdir.path()); | |||||
repo.add_sdist(tmp_sd.sdist, dds::if_exists(opts.if_exists)); | |||||
} | |||||
if (opts.pkg.import.from_stdin) { | |||||
auto tmp_sd = dds::expand_sdist_from_istream(std::cin, "<stdin>"); | |||||
repo.add_sdist(tmp_sd.sdist, dds::if_exists(opts.if_exists)); | |||||
} | |||||
return 0; | |||||
}); | |||||
} | |||||
int pkg_import(const options& opts) { | |||||
return boost::leaf::try_catch( | |||||
[&] { | |||||
try { | |||||
return _pkg_import(opts); | |||||
} catch (...) { | |||||
dds::capture_exception(); | |||||
} | |||||
}, | |||||
[&](const json5::parse_error& e) { | |||||
dds_log(error, "Error parsing JSON in package archive: {}", e.what()); | |||||
return 1; | |||||
}, | |||||
[](dds::e_sqlite3_error_exc e) { | |||||
dds_log(error, "Unexpected database error: {}", e.message); | |||||
return 1; | |||||
}); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include <dds/repo/repo.hpp> | |||||
#include <dds/source/dist.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <neo/assert.hpp> | |||||
#include <range/v3/range/conversion.hpp> | |||||
#include <range/v3/view/group_by.hpp> | |||||
#include <range/v3/view/transform.hpp> | |||||
#include <iostream> | |||||
#include <string_view> | |||||
namespace dds::cli::cmd { | |||||
static int _pkg_ls(const options& opts) { | |||||
auto list_contents = [&](repository repo) { | |||||
auto same_name | |||||
= [](auto&& a, auto&& b) { return a.manifest.pkg_id.name == b.manifest.pkg_id.name; }; | |||||
auto all = repo.iter_sdists(); | |||||
auto grp_by_name = all // | |||||
| ranges::views::group_by(same_name) // | |||||
| ranges::views::transform(ranges::to_vector) // | |||||
| ranges::views::transform([](auto&& grp) { | |||||
assert(grp.size() > 0); | |||||
return std::pair(grp[0].manifest.pkg_id.name, grp); | |||||
}); | |||||
for (const auto& [name, grp] : grp_by_name) { | |||||
dds_log(info, "{}:", name); | |||||
for (const dds::sdist& sd : grp) { | |||||
dds_log(info, " - {}", sd.manifest.pkg_id.version.to_string()); | |||||
} | |||||
} | |||||
return 0; | |||||
}; | |||||
return dds::repository::with_repository(opts.pkg_cache_dir.value_or( | |||||
repository::default_local_path()), | |||||
dds::repo_flags::read, | |||||
list_contents); | |||||
} | |||||
int pkg_ls(const options& opts) { | |||||
return boost::leaf::try_catch( | |||||
[&] { | |||||
try { | |||||
return _pkg_ls(opts); | |||||
} catch (...) { | |||||
dds::capture_exception(); | |||||
} | |||||
}, | |||||
[](dds::e_sqlite3_error_exc e) { | |||||
dds_log(error, "Unexpected database error: {}", e.message); | |||||
return 1; | |||||
}); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include "./pkg_repo_err_handle.hpp" | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/remote/remote.hpp> | |||||
namespace dds::cli::cmd { | |||||
static int _pkg_repo_add(const options& opts) { | |||||
auto cat = opts.open_catalog(); | |||||
auto repo = remote_repository::connect(opts.pkg.repo.add.url); | |||||
repo.store(cat.database()); | |||||
if (opts.pkg.repo.add.update) { | |||||
repo.update_catalog(cat.database()); | |||||
} | |||||
return 0; | |||||
} | |||||
int pkg_repo_add(const options& opts) { | |||||
return handle_pkg_repo_remote_errors([&] { return _pkg_repo_add(opts); }); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "./pkg_repo_err_handle.hpp" | |||||
#include <dds/http/session.hpp> | |||||
#include <dds/util/log.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <json5/parse_data.hpp> | |||||
#include <neo/url/parse.hpp> | |||||
int dds::cli::cmd::handle_pkg_repo_remote_errors(std::function<int()> fn) { | |||||
return boost::leaf::try_catch( | |||||
[&] { | |||||
try { | |||||
return fn(); | |||||
} catch (...) { | |||||
dds::capture_exception(); | |||||
} | |||||
}, | |||||
[&](neo::url_validation_error url_err, dds::e_url_string bad_url) { | |||||
dds_log(error, "Invalid URL [{}]: {}", bad_url.value, url_err.what()); | |||||
return 1; | |||||
}, | |||||
[&](const json5::parse_error& e, dds::e_http_url bad_url) { | |||||
dds_log(error, | |||||
"Error parsing JSON downloaded from URL [{}]: {}", | |||||
bad_url.value, | |||||
e.what()); | |||||
return 1; | |||||
}, | |||||
[](dds::e_sqlite3_error_exc e, dds::e_url_string url) { | |||||
dds_log(error, "Error accessing remote database (From {}): {}", url.value, e.message); | |||||
return 1; | |||||
}, | |||||
[](dds::e_sqlite3_error_exc e) { | |||||
dds_log(error, "Unexpected database error: {}", e.message); | |||||
return 1; | |||||
}, | |||||
[&](dds::e_system_error_exc e, dds::e_http_connect conn) { | |||||
dds_log(error, | |||||
"Error opening connection to [{}:{}]: {}", | |||||
conn.host, | |||||
conn.port, | |||||
e.message); | |||||
return 1; | |||||
}); | |||||
} |
#pragma once | |||||
#include <functional> | |||||
namespace dds::cli::cmd { | |||||
int handle_pkg_repo_remote_errors(std::function<int()>); | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include "./pkg_repo_err_handle.hpp" | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/remote/remote.hpp> | |||||
namespace dds::cli::cmd { | |||||
static int _pkg_repo_update(const options& opts) { | |||||
update_all_remotes(opts.open_catalog().database()); | |||||
return 0; | |||||
} | |||||
int pkg_repo_update(const options& opts) { | |||||
return handle_pkg_repo_remote_errors([&] { return _pkg_repo_update(opts); }); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include <dds/repoman/repoman.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <fmt/ostream.h> | |||||
#include <neo/sqlite3/error.hpp> | |||||
namespace dds::cli::cmd { | |||||
static int _repoman_import(const options& opts) { | |||||
auto repo = repo_manager::open(opts.repoman.repo_dir); | |||||
for (auto pkg : opts.repoman.import.files) { | |||||
repo.import_targz(pkg); | |||||
} | |||||
return 0; | |||||
} | |||||
int repoman_import(const options& opts) { | |||||
return boost::leaf::try_catch( // | |||||
[&] { | |||||
try { | |||||
return _repoman_import(opts); | |||||
} catch (...) { | |||||
dds::capture_exception(); | |||||
} | |||||
}, | |||||
[](dds::e_sqlite3_error_exc, | |||||
boost::leaf::match<neo::sqlite3::errc, neo::sqlite3::errc::constraint_unique>, | |||||
dds::e_repo_import_targz tgz, | |||||
dds::package_id pkg_id) { | |||||
dds_log(error, | |||||
"Package {} (from {}) is already present in the repository", | |||||
pkg_id.to_string(), | |||||
tgz.path); | |||||
return 1; | |||||
}, | |||||
[](dds::e_system_error_exc e, dds::e_repo_import_targz tgz) { | |||||
dds_log(error, "Failed to import file {}: {}", tgz.path, e.message); | |||||
return 1; | |||||
}, | |||||
[](const std::runtime_error& e, dds::e_repo_import_targz tgz) { | |||||
dds_log(error, "Unknown error while importing file {}: {}", tgz.path, e.what()); | |||||
return 1; | |||||
}, | |||||
[](dds::e_sqlite3_error_exc e, dds::e_repo_import_targz tgz) { | |||||
dds_log(error, "Database error while importing tar file {}: {}", tgz.path, e.message); | |||||
return 1; | |||||
}, | |||||
[](dds::e_system_error_exc e, dds::e_open_repo_db db) { | |||||
dds_log(error, "Error while opening repository database {}: {}", db.path, e.message); | |||||
return 1; | |||||
}); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include <dds/repoman/repoman.hpp> | |||||
#include <dds/util/log.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <fmt/ostream.h> | |||||
namespace dds::cli::cmd { | |||||
static int _repoman_init(const options& opts) { | |||||
auto repo = repo_manager::create(opts.repoman.repo_dir, opts.repoman.init.name); | |||||
dds_log(info, "Created new repository '{}' in {}", repo.name(), repo.root()); | |||||
return 0; | |||||
} | |||||
int repoman_init(const options& opts) { | |||||
return boost::leaf::try_catch( // | |||||
[&] { | |||||
try { | |||||
return _repoman_init(opts); | |||||
} catch (...) { | |||||
dds::capture_exception(); | |||||
} | |||||
}, | |||||
[](dds::e_sqlite3_error_exc e, dds::e_init_repo init, dds::e_init_repo_db init_db) { | |||||
dds_log(error, | |||||
"SQLite error while initializing repository in [{}] (SQlite database {}): {}", | |||||
init.path, | |||||
init_db.path, | |||||
e.message); | |||||
return 1; | |||||
}, | |||||
[](dds::e_system_error_exc e, dds::e_open_repo_db db) { | |||||
dds_log(error, "Error while opening repository database {}: {}", db.path, e.message); | |||||
return 1; | |||||
}, | |||||
[](dds::e_sqlite3_error_exc e, dds::e_init_repo init) { | |||||
dds_log(error, | |||||
"SQLite error while initializing repository in [{}]: {}", | |||||
init.path, | |||||
e.message); | |||||
return 1; | |||||
}); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include <dds/repoman/repoman.hpp> | |||||
#include <dds/util/log.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <fmt/ostream.h> | |||||
#include <iostream> | |||||
namespace dds::cli::cmd { | |||||
static int _repoman_ls(const options& opts) { | |||||
auto repo = repo_manager::open(opts.repoman.repo_dir); | |||||
for (auto pkg_id : repo.all_packages()) { | |||||
std::cout << pkg_id.to_string() << '\n'; | |||||
} | |||||
return 0; | |||||
} | |||||
int repoman_ls(const options& opts) { | |||||
return boost::leaf::try_catch( // | |||||
[&] { | |||||
try { | |||||
return _repoman_ls(opts); | |||||
} catch (...) { | |||||
dds::capture_exception(); | |||||
} | |||||
}, | |||||
[](dds::e_system_error_exc e, dds::e_open_repo_db db) { | |||||
dds_log(error, "Error while opening repository database {}: {}", db.path, e.message); | |||||
return 1; | |||||
}); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include <dds/repoman/repoman.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <fmt/ostream.h> | |||||
#include <neo/sqlite3/error.hpp> | |||||
namespace dds::cli::cmd { | |||||
static int _repoman_remove(const options& opts) { | |||||
auto repo = repo_manager::open(opts.repoman.repo_dir); | |||||
for (auto& str : opts.repoman.remove.pkgs) { | |||||
auto pkg_id = dds::package_id::parse(str); | |||||
repo.delete_package(pkg_id); | |||||
} | |||||
return 0; | |||||
} | |||||
int repoman_remove(const options& opts) { | |||||
return boost::leaf::try_catch( // | |||||
[&] { | |||||
try { | |||||
return _repoman_remove(opts); | |||||
} catch (...) { | |||||
dds::capture_exception(); | |||||
} | |||||
}, | |||||
[](dds::e_sqlite3_error_exc, | |||||
boost::leaf::match<neo::sqlite3::errc, neo::sqlite3::errc::constraint_unique>, | |||||
dds::e_repo_import_targz tgz, | |||||
dds::package_id pkg_id) { | |||||
dds_log(error, | |||||
"Package {} (from {}) is already present in the repository", | |||||
pkg_id.to_string(), | |||||
tgz.path); | |||||
return 1; | |||||
}, | |||||
[](dds::e_system_error_exc e, dds::e_repo_delete_path tgz, dds::package_id pkg_id) { | |||||
dds_log(error, | |||||
"Cannot delete requested package '{}' from repository (Path {}): {}", | |||||
pkg_id.to_string(), | |||||
tgz.path, | |||||
e.message); | |||||
return 1; | |||||
}, | |||||
[](dds::e_system_error_exc e, dds::e_open_repo_db db) { | |||||
dds_log(error, "Error while opening repository database {}: {}", db.path, e.message); | |||||
return 1; | |||||
}); | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "../options.hpp" | |||||
#include <dds/source/dist.hpp> | |||||
#include <fmt/core.h> | |||||
namespace dds::cli::cmd { | |||||
int sdist_create(const options& opts) { | |||||
dds::sdist_params params{ | |||||
.project_dir = opts.project_dir, | |||||
.dest_path = {}, | |||||
.force = opts.if_exists == if_exists::replace, | |||||
.include_apps = true, | |||||
.include_tests = true, | |||||
}; | |||||
auto pkg_man = package_manifest::load_from_directory(params.project_dir); | |||||
auto default_filename = fmt::format("{}.tar.gz", pkg_man->pkg_id.to_string()); | |||||
auto filepath = opts.out_path.value_or(fs::current_path() / default_filename); | |||||
create_sdist_targz(filepath, params); | |||||
return 0; | |||||
} | |||||
} // namespace dds::cli::cmd |
#include "./dispatch_main.hpp" | |||||
#include "./error_handler.hpp" | |||||
#include "./options.hpp" | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/remote/remote.hpp> | |||||
#include <dds/util/paths.hpp> | |||||
#include <dds/util/result.hpp> | |||||
using namespace dds; | |||||
namespace dds::cli { | |||||
namespace cmd { | |||||
using command = int(const options&); | |||||
command build_deps; | |||||
command build; | |||||
command compile_file; | |||||
command pkg_get; | |||||
command pkg_import; | |||||
command pkg_ls; | |||||
command pkg_repo_add; | |||||
command pkg_repo_update; | |||||
command repoman_import; | |||||
command repoman_init; | |||||
command repoman_ls; | |||||
command repoman_remove; | |||||
command sdist_create; | |||||
} // namespace cmd | |||||
int dispatch_main(const options& opts) noexcept { | |||||
dds::log::current_log_level = opts.log_level; | |||||
return dds::handle_cli_errors([&] { | |||||
switch (opts.subcommand) { | |||||
case subcommand::build: | |||||
return cmd::build(opts); | |||||
case subcommand::sdist: | |||||
switch (opts.sdist.subcommand) { | |||||
case sdist_subcommand::create: | |||||
return cmd::sdist_create(opts); | |||||
case sdist_subcommand::_none_:; | |||||
} | |||||
neo::unreachable(); | |||||
case subcommand::pkg: | |||||
switch (opts.pkg.subcommand) { | |||||
case pkg_subcommand::ls: | |||||
return cmd::pkg_ls(opts); | |||||
case pkg_subcommand::get: | |||||
return cmd::pkg_get(opts); | |||||
case pkg_subcommand::import: | |||||
return cmd::pkg_import(opts); | |||||
case pkg_subcommand::repo: | |||||
switch (opts.pkg.repo.subcommand) { | |||||
case cli_pkg_repo_subcommand::add: | |||||
return cmd::pkg_repo_add(opts); | |||||
case cli_pkg_repo_subcommand::update: | |||||
return cmd::pkg_repo_update(opts); | |||||
case cli_pkg_repo_subcommand::_none_:; | |||||
} | |||||
neo::unreachable(); | |||||
case pkg_subcommand::_none_:; | |||||
} | |||||
neo::unreachable(); | |||||
case subcommand::repoman: | |||||
switch (opts.repoman.subcommand) { | |||||
case repoman_subcommand::import: | |||||
return cmd::repoman_import(opts); | |||||
case repoman_subcommand::init: | |||||
return cmd::repoman_init(opts); | |||||
case repoman_subcommand::remove: | |||||
return cmd::repoman_remove(opts); | |||||
case repoman_subcommand::ls: | |||||
return cmd::repoman_ls(opts); | |||||
case repoman_subcommand::_none_:; | |||||
} | |||||
neo::unreachable(); | |||||
case subcommand::compile_file: | |||||
return cmd::compile_file(opts); | |||||
case subcommand::build_deps: | |||||
return cmd::build_deps(opts); | |||||
case subcommand::_none_:; | |||||
} | |||||
neo::unreachable(); | |||||
return 6; | |||||
}); | |||||
} | |||||
} // namespace dds::cli |
#pragma once | |||||
namespace dds::cli { | |||||
struct options; | |||||
int dispatch_main(const options&) noexcept; | |||||
} // namespace dds |
#include "./error_handler.hpp" | |||||
#include "./options.hpp" | |||||
#include <dds/error/errors.hpp> | |||||
#include <dds/util/log.hpp> | |||||
#include <dds/util/result.hpp> | |||||
#include <dds/util/signal.hpp> | |||||
#include <boost/leaf/handle_error.hpp> | |||||
#include <boost/leaf/handle_exception.hpp> | |||||
#include <boost/leaf/result.hpp> | |||||
#include <fmt/ostream.h> | |||||
#include <neo/url/parse.hpp> | |||||
namespace { | |||||
template <dds::cli::subcommand Val> | |||||
using subcommand = boost::leaf::match<dds::cli::subcommand, Val>; | |||||
auto handlers = std::tuple( // | |||||
[](neo::url_validation_error exc, dds::e_url_string bad_url) { | |||||
dds_log(error, "Invalid URL '{}': {}", bad_url.value, exc.what()); | |||||
return 1; | |||||
}, | |||||
[](boost::leaf::catch_<dds::error_base> exc) { | |||||
dds_log(error, "{}", exc.value().what()); | |||||
dds_log(error, "{}", exc.value().explanation()); | |||||
dds_log(error, "Refer: {}", exc.value().error_reference()); | |||||
return 1; | |||||
}, | |||||
[](dds::user_cancelled) { | |||||
dds_log(critical, "Operation cancelled by the user"); | |||||
return 2; | |||||
}, | |||||
[](boost::leaf::verbose_diagnostic_info const& diag) { | |||||
dds_log(critical, "An unhandled error arose. THIS IS A DDS BUG! Info: {}", diag); | |||||
return 42; | |||||
}); | |||||
} // namespace | |||||
int dds::handle_cli_errors(std::function<int()> fn) noexcept { | |||||
return boost::leaf::try_handle_all([&]() -> boost::leaf::result<int> { return fn(); }, | |||||
handlers); | |||||
} |
#pragma once | |||||
#include <functional> | |||||
namespace dds { | |||||
int handle_cli_errors(std::function<int()>) noexcept; | |||||
} // namespace dds |
#include "./options.hpp" | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/error/errors.hpp> | |||||
#include <dds/toolchain/from_json.hpp> | |||||
#include <dds/toolchain/toolchain.hpp> | |||||
#include <debate/enum.hpp> | |||||
using namespace dds; | |||||
using namespace debate; | |||||
namespace { | |||||
struct setup { | |||||
dds::cli::options& opts; | |||||
// Util argument common to a lot of operations | |||||
argument if_exists_arg{ | |||||
.long_spellings = {"if-exists"}, | |||||
.help = "What to do if the resource already exists", | |||||
.valname = "{replace,skip,fail}", | |||||
.action = put_into(opts.if_exists), | |||||
}; | |||||
argument toolchain_arg{ | |||||
.long_spellings = {"toolchain"}, | |||||
.short_spellings = {"t"}, | |||||
.help = "The toolchain to use when building", | |||||
.valname = "<file-or-id>", | |||||
.action = put_into(opts.toolchain), | |||||
}; | |||||
argument project_arg{ | |||||
.long_spellings = {"project"}, | |||||
.short_spellings = {"p"}, | |||||
.help = "The project to build. If not given, uses the current working directory", | |||||
.valname = "<project-path>", | |||||
.action = put_into(opts.project_dir), | |||||
}; | |||||
argument no_warn_arg{ | |||||
.long_spellings = {"no-warn", "no-warnings"}, | |||||
.help = "Disable build warnings", | |||||
.nargs = 0, | |||||
.action = store_true(opts.disable_warnings), | |||||
}; | |||||
argument out_arg{ | |||||
.long_spellings = {"out", "output"}, | |||||
.short_spellings = {"o"}, | |||||
.help = "Path to the output", | |||||
.valname = "<path>", | |||||
.action = put_into(opts.out_path), | |||||
}; | |||||
argument lm_index_arg{ | |||||
.long_spellings = {"libman-index"}, | |||||
.help = "Path to a libman index to use", | |||||
.valname = "<lmi-path>", | |||||
.action = put_into(opts.build.lm_index), | |||||
}; | |||||
argument jobs_arg{ | |||||
.long_spellings = {"jobs"}, | |||||
.short_spellings = {"j"}, | |||||
.help = "Set the maximum number of parallel jobs to execute", | |||||
.valname = "<job-count>", | |||||
.action = put_into(opts.jobs), | |||||
}; | |||||
argument repoman_repo_dir_arg{ | |||||
.help = "The directory of the repository to manage", | |||||
.valname = "<repo-dir>", | |||||
.required = true, | |||||
.action = put_into(opts.repoman.repo_dir), | |||||
}; | |||||
void do_setup(argument_parser& parser) noexcept { | |||||
parser.add_argument({ | |||||
.long_spellings = {"log-level"}, | |||||
.short_spellings = {"l"}, | |||||
.help = "" | |||||
"Set the dds logging level. One of 'trace', 'debug', 'info', \n" | |||||
"'warn', 'error', 'critical', or 'silent'", | |||||
.valname = "<level>", | |||||
.action = put_into(opts.log_level), | |||||
}); | |||||
parser.add_argument({ | |||||
.long_spellings = {"data-dir"}, | |||||
.help | |||||
= "" | |||||
"(Advanced) " | |||||
"Override dds's data directory. This is used for various caches and databases.\n" | |||||
"The default is a user-local directory that differs depending on platform.", | |||||
.valname = "<directory>", | |||||
.action = put_into(opts.data_dir), | |||||
}); | |||||
parser.add_argument({ | |||||
.long_spellings = {"pkg-cache-dir"}, | |||||
.help = "(Advanced) Override dds's local package cache directory.", | |||||
.valname = "<directory>", | |||||
.action = put_into(opts.pkg_cache_dir), | |||||
}); | |||||
parser.add_argument({ | |||||
.long_spellings = {"pkg-db-path"}, | |||||
.help = "(Advanced) Override dds's default package database path.", | |||||
.valname = "<database-path>", | |||||
.action = put_into(opts.pkg_db_dir), | |||||
}); | |||||
setup_main_commands(parser.add_subparsers({ | |||||
.description = "The operation to perform", | |||||
.action = put_into(opts.subcommand), | |||||
})); | |||||
} | |||||
void setup_main_commands(subparser_group& group) { | |||||
setup_build_cmd(group.add_parser({ | |||||
.name = "build", | |||||
.help = "Build a project", | |||||
})); | |||||
setup_compile_file_cmd(group.add_parser({ | |||||
.name = "compile-file", | |||||
.help = "Compile individual files in the project", | |||||
})); | |||||
setup_build_deps_cmd(group.add_parser({ | |||||
.name = "build-deps", | |||||
.help = "Build a set of dependencies and generate a libman index", | |||||
})); | |||||
setup_pkg_cmd(group.add_parser({ | |||||
.name = "pkg", | |||||
.help = "Manage packages and package remotes", | |||||
})); | |||||
setup_sdist_cmd(group.add_parser({ | |||||
.name = "sdist", | |||||
.help = "Work with source distribution packages", | |||||
})); | |||||
setup_repoman_cmd(group.add_parser({ | |||||
.name = "repoman", | |||||
.help = "Manage a dds package repository", | |||||
})); | |||||
} | |||||
void setup_build_cmd(argument_parser& build_cmd) { | |||||
build_cmd.add_argument(toolchain_arg.dup()); | |||||
build_cmd.add_argument(project_arg.dup()); | |||||
build_cmd.add_argument({ | |||||
.long_spellings = {"no-tests"}, | |||||
.help = "Do not build and run project tests", | |||||
.nargs = 0, | |||||
.action = debate::store_false(opts.build.want_tests), | |||||
}); | |||||
build_cmd.add_argument({ | |||||
.long_spellings = {"no-apps"}, | |||||
.help = "Do not build project applications", | |||||
.nargs = 0, | |||||
.action = debate::store_false(opts.build.want_apps), | |||||
}); | |||||
build_cmd.add_argument(no_warn_arg.dup()); | |||||
build_cmd.add_argument(out_arg.dup()).help = "Directory where dds will write build results"; | |||||
build_cmd.add_argument({ | |||||
.long_spellings = {"add-repo"}, | |||||
.help = "" | |||||
"Add remote repositories to the package catalog before building\n" | |||||
"(Implies --update-repos)", | |||||
.valname = "<repo-url>", | |||||
.can_repeat = true, | |||||
.action = debate::push_back_onto(opts.build.add_repos), | |||||
}); | |||||
build_cmd.add_argument({ | |||||
.long_spellings = {"update-repos"}, | |||||
.short_spellings = {"U"}, | |||||
.help = "Update package repositories before building", | |||||
.nargs = 0, | |||||
.action = debate::store_true(opts.build.update_repos), | |||||
}); | |||||
build_cmd.add_argument(lm_index_arg.dup()).help | |||||
= "Path to a libman index file to use for loading project dependencies"; | |||||
build_cmd.add_argument(jobs_arg.dup()); | |||||
} | |||||
void setup_compile_file_cmd(argument_parser& compile_file_cmd) noexcept { | |||||
compile_file_cmd.add_argument(project_arg.dup()); | |||||
compile_file_cmd.add_argument(toolchain_arg.dup()); | |||||
compile_file_cmd.add_argument(no_warn_arg.dup()).help = "Disable compiler warnings"; | |||||
compile_file_cmd.add_argument(jobs_arg.dup()).help | |||||
= "Set the maximum number of files to compile in parallel"; | |||||
compile_file_cmd.add_argument(lm_index_arg.dup()); | |||||
compile_file_cmd.add_argument(out_arg.dup()); | |||||
compile_file_cmd.add_argument({ | |||||
.help = "One or more source files to compile", | |||||
.valname = "<source-files>", | |||||
.can_repeat = true, | |||||
.action = debate::push_back_onto(opts.compile_file.files), | |||||
}); | |||||
} | |||||
void setup_build_deps_cmd(argument_parser& build_deps_cmd) noexcept { | |||||
build_deps_cmd.add_argument(toolchain_arg.dup()).required; | |||||
build_deps_cmd.add_argument(jobs_arg.dup()); | |||||
build_deps_cmd.add_argument(out_arg.dup()); | |||||
build_deps_cmd.add_argument(lm_index_arg.dup()).help | |||||
= "Destination path for the generated libman index file"; | |||||
build_deps_cmd.add_argument({ | |||||
.long_spellings = {"deps-file"}, | |||||
.short_spellings = {"d"}, | |||||
.help = "Path to a JSON5 file listing dependencies", | |||||
.valname = "<deps-file>", | |||||
.can_repeat = true, | |||||
.action = debate::push_back_onto(opts.build_deps.deps_files), | |||||
}); | |||||
build_deps_cmd.add_argument({ | |||||
.help = "Dependency statement strings", | |||||
.valname = "<dependency>", | |||||
.can_repeat = true, | |||||
.action = debate::push_back_onto(opts.build_deps.deps), | |||||
}); | |||||
} | |||||
void setup_pkg_cmd(argument_parser& pkg_cmd) { | |||||
auto& pkg_group = pkg_cmd.add_subparsers({ | |||||
.valname = "<pkg-subcommand>", | |||||
.action = put_into(opts.pkg.subcommand), | |||||
}); | |||||
pkg_group.add_parser({ | |||||
.name = "ls", | |||||
.help = "List locally available packages", | |||||
}); | |||||
setup_pkg_get_cmd(pkg_group.add_parser({ | |||||
.name = "get", | |||||
.help = "Obtain a copy of a package from a remote", | |||||
})); | |||||
setup_pkg_init_db_cmd(pkg_group.add_parser({ | |||||
.name = "init-db", | |||||
.help = "Initialize a new package database file (Path specified with '--pkg-db-path')", | |||||
})); | |||||
setup_pkg_import_cmd(pkg_group.add_parser({ | |||||
.name = "import", | |||||
.help = "Import a source distribution archive into the local package cache", | |||||
})); | |||||
setup_pkg_repo_cmd(pkg_group.add_parser({ | |||||
.name = "repo", | |||||
.help = "Manage package repositories", | |||||
})); | |||||
} | |||||
void setup_pkg_get_cmd(argument_parser& pkg_get_cmd) { | |||||
pkg_get_cmd.add_argument({ | |||||
.valname = "<pkg-id>", | |||||
.can_repeat = true, | |||||
.action = push_back_onto(opts.pkg.get.pkgs), | |||||
}); | |||||
pkg_get_cmd.add_argument(out_arg.dup()).help | |||||
= "Directory where obtained packages will be placed.\n" | |||||
"Default is the current working directory."; | |||||
} | |||||
void setup_pkg_init_db_cmd(argument_parser& pkg_init_db_cmd) { | |||||
pkg_init_db_cmd.add_argument(if_exists_arg.dup()).help | |||||
= "What to do if the database file already exists"; | |||||
} | |||||
void setup_pkg_import_cmd(argument_parser& pkg_import_cmd) noexcept { | |||||
pkg_import_cmd.add_argument({ | |||||
.long_spellings = {"stdin"}, | |||||
.help = "Import a source distribution archive from standard input", | |||||
.nargs = 0, | |||||
.action = debate::store_true(opts.pkg.import.from_stdin), | |||||
}); | |||||
pkg_import_cmd.add_argument(if_exists_arg.dup()).help | |||||
= "What to do if the package already exists in the local cache"; | |||||
pkg_import_cmd.add_argument({ | |||||
.help = "One or more paths/URLs to source distribution archives to import", | |||||
.valname = "<path-or-url>", | |||||
.can_repeat = true, | |||||
.action = debate::push_back_onto(opts.pkg.import.items), | |||||
}); | |||||
} | |||||
void setup_pkg_repo_cmd(argument_parser& pkg_repo_cmd) noexcept { | |||||
auto& pkg_repo_grp = pkg_repo_cmd.add_subparsers({ | |||||
.valname = "<pkg-repo-subcommand>", | |||||
.action = put_into(opts.pkg.repo.subcommand), | |||||
}); | |||||
setup_pkg_repo_add_cmd(pkg_repo_grp.add_parser({ | |||||
.name = "add", | |||||
.help = "Add a package repository", | |||||
})); | |||||
pkg_repo_grp.add_parser({ | |||||
.name = "update", | |||||
.help = "Update package repository information", | |||||
}); | |||||
} | |||||
void setup_pkg_repo_add_cmd(argument_parser& pkg_repo_add_cmd) noexcept { | |||||
pkg_repo_add_cmd.add_argument({ | |||||
.help = "URL of a repository to add", | |||||
.valname = "<url>", | |||||
.required = true, | |||||
.action = debate::put_into(opts.pkg.repo.add.url), | |||||
}); | |||||
pkg_repo_add_cmd.add_argument({ | |||||
.long_spellings = {"no-update"}, | |||||
.help = "Do not immediately update for the new package repository", | |||||
.nargs = 0, | |||||
.action = debate::store_false(opts.pkg.repo.add.update), | |||||
}); | |||||
} | |||||
void setup_sdist_cmd(argument_parser& sdist_cmd) noexcept { | |||||
auto& sdist_grp = sdist_cmd.add_subparsers({ | |||||
.valname = "<sdist-subcommand>", | |||||
.action = put_into(opts.sdist.subcommand), | |||||
}); | |||||
setup_sdist_create_cmd(sdist_grp.add_parser({ | |||||
.name = "create", | |||||
.help = "Create a source distribution from a project tree", | |||||
})); | |||||
} | |||||
void setup_sdist_create_cmd(argument_parser& sdist_create_cmd) { | |||||
sdist_create_cmd.add_argument(project_arg.dup()).help | |||||
= "Path to the project for which to create a source distribution.\n" | |||||
"Default is the current working directory."; | |||||
sdist_create_cmd.add_argument(out_arg.dup()).help | |||||
= "Destination path for the source distributnion archive"; | |||||
sdist_create_cmd.add_argument(if_exists_arg.dup()).help | |||||
= "What to do if the destination names an existing file"; | |||||
} | |||||
void setup_repoman_cmd(argument_parser& repoman_cmd) { | |||||
auto& grp = repoman_cmd.add_subparsers({ | |||||
.valname = "<repoman-subcommand>", | |||||
.action = put_into(opts.repoman.subcommand), | |||||
}); | |||||
setup_repoman_init_cmd(grp.add_parser({ | |||||
.name = "init", | |||||
.help = "Initialize a directory as a new repository", | |||||
})); | |||||
setup_repoman_import_cmd(grp.add_parser({ | |||||
.name = "import", | |||||
.help = "Import a source distribution into the repository", | |||||
})); | |||||
auto& ls_cmd = grp.add_parser({ | |||||
.name = "ls", | |||||
.help = "List the contents of a package repository directory", | |||||
}); | |||||
ls_cmd.add_argument(repoman_repo_dir_arg.dup()); | |||||
setup_repoman_remove_cmd(grp.add_parser({ | |||||
.name = "remove", | |||||
.help = "Remove packages from a package repository", | |||||
})); | |||||
} | |||||
void setup_repoman_init_cmd(argument_parser& repoman_init_cmd) { | |||||
repoman_init_cmd.add_argument(repoman_repo_dir_arg.dup()); | |||||
repoman_init_cmd.add_argument(if_exists_arg.dup()).help | |||||
= "What to do if the directory exists and is already repository"; | |||||
repoman_init_cmd.add_argument({ | |||||
.long_spellings = {"name"}, | |||||
.short_spellings = {"n"}, | |||||
.help = "Specifiy the name of the new repository", | |||||
.valname = "<name>", | |||||
.action = put_into(opts.repoman.init.name), | |||||
}); | |||||
} | |||||
void setup_repoman_import_cmd(argument_parser& repoman_import_cmd) { | |||||
repoman_import_cmd.add_argument(repoman_repo_dir_arg.dup()); | |||||
repoman_import_cmd.add_argument({ | |||||
.help = "Paths to source distribution archives to import", | |||||
.valname = "<sdist-file-path>", | |||||
.can_repeat = true, | |||||
.action = push_back_onto(opts.repoman.import.files), | |||||
}); | |||||
} | |||||
void setup_repoman_remove_cmd(argument_parser& repoman_remove_cmd) { | |||||
repoman_remove_cmd.add_argument(repoman_repo_dir_arg.dup()); | |||||
repoman_remove_cmd.add_argument({ | |||||
.help = "One or more identifiers of packages to remove", | |||||
.valname = "<pkg-id>", | |||||
.can_repeat = true, | |||||
.action = push_back_onto(opts.repoman.remove.pkgs), | |||||
}); | |||||
} | |||||
}; | |||||
} // namespace | |||||
void cli::options::setup_parser(debate::argument_parser& parser) noexcept { | |||||
setup{*this}.do_setup(parser); | |||||
} | |||||
catalog dds::cli::options::open_catalog() const { | |||||
return catalog::open(this->pkg_db_dir.value_or(catalog::default_path())); | |||||
} | |||||
toolchain dds::cli::options::load_toolchain() const { | |||||
if (!toolchain) { | |||||
auto def = dds::toolchain::get_default(); | |||||
if (!def) { | |||||
throw_user_error<errc::no_default_toolchain>(); | |||||
} | |||||
return *def; | |||||
} | |||||
// Convert the given string to a toolchain | |||||
auto& tc_str = *toolchain; | |||||
if (tc_str.starts_with(":")) { | |||||
auto default_tc = tc_str.substr(1); | |||||
auto tc = dds::toolchain::get_builtin(default_tc); | |||||
if (!tc.has_value()) { | |||||
throw_user_error< | |||||
errc::invalid_builtin_toolchain>("Invalid built-in toolchain name '{}'", | |||||
default_tc); | |||||
} | |||||
return std::move(*tc); | |||||
} else { | |||||
return parse_toolchain_json5(slurp_file(tc_str)); | |||||
} | |||||
} |
#pragma once | |||||
#include <dds/util/log.hpp> | |||||
#include <debate/argument_parser.hpp> | |||||
#include <filesystem> | |||||
#include <optional> | |||||
#include <string> | |||||
#include <vector> | |||||
namespace dds { | |||||
namespace fs = std::filesystem; | |||||
class catalog; | |||||
class toolchain; | |||||
namespace cli { | |||||
/** | |||||
* @brief Top-level dds subcommands | |||||
*/ | |||||
enum class subcommand { | |||||
_none_, | |||||
build, | |||||
compile_file, | |||||
build_deps, | |||||
pkg, | |||||
sdist, | |||||
repoman, | |||||
}; | |||||
/** | |||||
* @brief 'dds sdist' subcommands | |||||
*/ | |||||
enum class sdist_subcommand { | |||||
_none_, | |||||
create, | |||||
}; | |||||
/** | |||||
* @brief 'dds pkg' subcommands | |||||
*/ | |||||
enum class pkg_subcommand { | |||||
_none_, | |||||
ls, | |||||
get, | |||||
import, | |||||
repo, | |||||
}; | |||||
/** | |||||
* @brief 'dds pkg repo' subcommands | |||||
*/ | |||||
enum class cli_pkg_repo_subcommand { | |||||
_none_, | |||||
add, | |||||
update, | |||||
}; | |||||
/** | |||||
* @brief 'dds repoman' subcommands | |||||
* | |||||
*/ | |||||
enum class repoman_subcommand { | |||||
_none_, | |||||
init, | |||||
import, | |||||
remove, | |||||
ls, | |||||
}; | |||||
/** | |||||
* @brief Options for `--if-exists` on the CLI | |||||
*/ | |||||
enum class if_exists { | |||||
replace, | |||||
fail, | |||||
ignore, | |||||
}; | |||||
/** | |||||
* @brief Complete aggregate of all dds command-line options, and some utilities | |||||
*/ | |||||
struct options { | |||||
using path = fs::path; | |||||
using opt_path = std::optional<fs::path>; | |||||
using string = std::string; | |||||
using opt_string = std::optional<std::string>; | |||||
// The `--data-dir` argument | |||||
opt_path data_dir; | |||||
// The `--pkg-cache-dir' argument | |||||
opt_path pkg_cache_dir; | |||||
// The `--pkg-db-dir` argument | |||||
opt_path pkg_db_dir; | |||||
// The `--log-level` argument | |||||
log::level log_level = log::level::info; | |||||
// The top-most selected subcommand | |||||
enum subcommand subcommand; | |||||
// Many subcommands use a '--project' argument, stored here, using the CWD as the default | |||||
path project_dir = fs::current_path(); | |||||
// Compile and build commands with `--no-warnings`/`--no-warn` | |||||
bool disable_warnings = true; | |||||
// Compile and build commands' `--jobs` parameter | |||||
int jobs = 0; | |||||
// Compile and build commands' `--toolchain` option: | |||||
opt_string toolchain; | |||||
opt_path out_path; | |||||
// Shared `--if-exists` argument: | |||||
cli::if_exists if_exists = cli::if_exists::fail; | |||||
/** | |||||
* @brief Open the package catalog based on the user-specified options. | |||||
* @return catalog | |||||
*/ | |||||
catalog open_catalog() const; | |||||
/** | |||||
* @brief Load a dds toolchain as specified by the user, or a default. | |||||
* @return dds::toolchain | |||||
*/ | |||||
dds::toolchain load_toolchain() const; | |||||
/** | |||||
* @brief Parameters specific to 'dds build' | |||||
*/ | |||||
struct { | |||||
bool want_tests = true; | |||||
bool want_apps = true; | |||||
opt_path lm_index; | |||||
std::vector<string> add_repos; | |||||
bool update_repos = false; | |||||
} build; | |||||
/** | |||||
* @brief Parameters specific to 'dds compile-file' | |||||
*/ | |||||
struct { | |||||
/// The files that the user has requested to be compiled | |||||
std::vector<fs::path> files; | |||||
} compile_file; | |||||
/** | |||||
* @brief Parameters specific to 'dds build-deps' | |||||
*/ | |||||
struct { | |||||
/// Files listed with '--deps-file' | |||||
std::vector<fs::path> deps_files; | |||||
/// Dependency strings provided directly in the command-line | |||||
std::vector<string> deps; | |||||
} build_deps; | |||||
/** | |||||
* @brief Parameters and subcommands for 'dds pkg' | |||||
* | |||||
*/ | |||||
struct { | |||||
/// The 'dds pkg' subcommand | |||||
pkg_subcommand subcommand; | |||||
/** | |||||
* @brief Parameters for 'dds pkg import' | |||||
*/ | |||||
struct { | |||||
/// File paths or URLs of packages to import | |||||
std::vector<string> items; | |||||
/// Allow piping a package tarball in through stdin | |||||
bool from_stdin = false; | |||||
} import; | |||||
/** | |||||
* @brief Parameters for 'dds pkg repo' | |||||
*/ | |||||
struct { | |||||
/// The 'pkg repo' subcommand | |||||
cli_pkg_repo_subcommand subcommand; | |||||
/** | |||||
* @brief Parameters of 'dds pkg repo add' | |||||
*/ | |||||
struct { | |||||
/// The repository URL | |||||
string url; | |||||
/// Whether we should update repo data after adding the repository | |||||
bool update = true; | |||||
} add; | |||||
} repo; | |||||
/** | |||||
* @brief Paramters for 'dds pkg get' | |||||
*/ | |||||
struct { | |||||
/// Package IDs to download | |||||
std::vector<string> pkgs; | |||||
} get; | |||||
} pkg; | |||||
struct { | |||||
sdist_subcommand subcommand; | |||||
} sdist; | |||||
/** | |||||
* @brief Parameters for 'dds repoman' | |||||
*/ | |||||
struct { | |||||
/// Shared parameter between repoman subcommands: The directory we are acting upon | |||||
path repo_dir; | |||||
/// The actual operation we are performing on the repository dir | |||||
repoman_subcommand subcommand; | |||||
/// Options for 'dds repoman init' | |||||
struct { | |||||
/// The name of the new repository. If not provided, a random one will be generated | |||||
opt_string name; | |||||
} init; | |||||
/// Options for 'dds repoman import' | |||||
struct { | |||||
/// sdist tarball file paths to import into the repository | |||||
std::vector<fs::path> files; | |||||
} import; | |||||
/// Options for 'dds repoman remove' | |||||
struct { | |||||
/// Package IDs of packages to remove | |||||
std::vector<string> pkgs; | |||||
} remove; | |||||
} repoman; | |||||
/** | |||||
* @brief Attach arguments and subcommands to the given argument parser, binding those arguments | |||||
* to the values in this object. | |||||
*/ | |||||
void setup_parser(debate::argument_parser& parser) noexcept; | |||||
}; | |||||
} // namespace cli | |||||
} // namespace dds |
auto name_dir = pkg_dir() / pkg_id.name; | auto name_dir = pkg_dir() / pkg_id.name; | ||||
auto ver_dir = name_dir / pkg_id.version.to_string(); | auto ver_dir = name_dir / pkg_id.version.to_string(); | ||||
DDS_E_SCOPE(e_repo_delete_targz{ver_dir}); | |||||
DDS_E_SCOPE(e_repo_delete_path{ver_dir}); | |||||
if (!fs::is_directory(ver_dir)) { | if (!fs::is_directory(ver_dir)) { | ||||
throw std::system_error(std::make_error_code(std::errc::no_such_file_or_directory), | throw std::system_error(std::make_error_code(std::errc::no_such_file_or_directory), |
fs::path path; | fs::path path; | ||||
}; | }; | ||||
struct e_repo_delete_targz { | |||||
struct e_repo_delete_path { | |||||
fs::path path; | fs::path path; | ||||
}; | }; | ||||
return spdlog::level::err; | return spdlog::level::err; | ||||
case level::critical: | case level::critical: | ||||
return spdlog::level::critical; | return spdlog::level::critical; | ||||
case level::_silent: | |||||
case level::silent: | |||||
return spdlog::level::off; | return spdlog::level::off; | ||||
} | } | ||||
neo_assert_always(invariant, false, "Invalid log level", msg, int(l)); | neo_assert_always(invariant, false, "Invalid log level", msg, int(l)); |
warn, | warn, | ||||
error, | error, | ||||
critical, | critical, | ||||
_silent, | |||||
silent, | |||||
}; | }; | ||||
inline level current_log_level = level::info; | inline level current_log_level = level::info; |
#include <neo/sqlite3/error.hpp> | #include <neo/sqlite3/error.hpp> | ||||
dds::error_id dds::capture_exception() { | |||||
void dds::capture_exception() { | |||||
try { | try { | ||||
throw; | throw; | ||||
} catch (const neo::sqlite3::sqlite3_error& e) { | } catch (const neo::sqlite3::sqlite3_error& e) { | ||||
return current_error().load(e_sqlite3_error_exc{std::string(e.what()), e.code()}, | |||||
e.code(), | |||||
neo::sqlite3::errc{e.code().value()}); | |||||
current_error().load(e_sqlite3_error_exc{std::string(e.what()), e.code()}, | |||||
e.code(), | |||||
neo::sqlite3::errc{e.code().value()}); | |||||
} catch (const std::system_error& e) { | } catch (const std::system_error& e) { | ||||
return current_error().load(e_system_error_exc{std::string(e.what()), e.code()}, e.code()); | |||||
current_error().load(e_system_error_exc{std::string(e.what()), e.code()}, e.code()); | |||||
} | } | ||||
// Re-throw as a bare exception. | |||||
throw std::exception(); | |||||
} | } |
* @brief Capture currently in-flight special exceptions as new error object. Works around a bug in | * @brief Capture currently in-flight special exceptions as new error object. Works around a bug in | ||||
* Boost.LEAF when catching std::system error. | * Boost.LEAF when catching std::system error. | ||||
*/ | */ | ||||
error_id capture_exception(); | |||||
[[noreturn]] void capture_exception(); | |||||
/** | /** | ||||
* @brief Generate a leaf::on_error object that loads the given expression into the currently | * @brief Generate a leaf::on_error object that loads the given expression into the currently |
tmp_project.write('src/f.cpp', r'void f() {}') | tmp_project.write('src/f.cpp', r'void f() {}') | ||||
tmp_project.build() | tmp_project.build() | ||||
# Writing again will build again: | # Writing again will build again: | ||||
time.sleep(0.2) # Sleep long enough to register a file change | |||||
time.sleep(0.5) # Sleep long enough to register a file change | |||||
tmp_project.write('src/f.cpp', r'bad again') | tmp_project.write('src/f.cpp', r'bad again') | ||||
with pytest.raises(CalledProcessError): | with pytest.raises(CalledProcessError): | ||||
tmp_project.build() | tmp_project.build() | ||||
} | } | ||||
def test_empty_with_pkg_dds(tmp_project: Project) -> None: | |||||
tmp_project.package_json = TEST_PACKAGE | |||||
tmp_project.build() | |||||
def test_empty_with_lib_dds(tmp_project: Project) -> None: | |||||
def test_empty_with_pkg_json(tmp_project: Project) -> None: | |||||
tmp_project.package_json = TEST_PACKAGE | tmp_project.package_json = TEST_PACKAGE | ||||
tmp_project.build() | tmp_project.build() | ||||
def test_empty_sdist_create(tmp_project: Project) -> None: | def test_empty_sdist_create(tmp_project: Project) -> None: | ||||
tmp_project.package_json = TEST_PACKAGE | tmp_project.package_json = TEST_PACKAGE | ||||
tmp_project.sdist_create() | tmp_project.sdist_create() | ||||
def test_empty_sdist_export(tmp_project: Project) -> None: | |||||
tmp_project.package_json = TEST_PACKAGE | |||||
tmp_project.sdist_export() | |||||
assert tmp_project.build_root.joinpath('test-pkg@0.2.2.tar.gz').is_file(), \ | |||||
'The expected sdist tarball was not generated' |
from pathlib import Path | |||||
from dds_ci.testing import Project, RepoFixture | from dds_ci.testing import Project, RepoFixture | ||||
from dds_ci.dds import DDSWrapper | |||||
def test_catalog_create(dds_2: DDSWrapper, tmp_path: Path) -> None: | |||||
cat_db = tmp_path / 'catalog.db' | |||||
assert not cat_db.is_file() | |||||
dds_2.run(['catalog', 'create', '--catalog', cat_db]) | |||||
assert cat_db.is_file() | |||||
def test_catalog_get_git(http_repo: RepoFixture, tmp_project: Project) -> None: | |||||
def test_pkg_get(http_repo: RepoFixture, tmp_project: Project) -> None: | |||||
http_repo.import_json_data({ | http_repo.import_json_data({ | ||||
'packages': { | 'packages': { | ||||
'neo-sqlite3': { | 'neo-sqlite3': { | ||||
} | } | ||||
}) | }) | ||||
tmp_project.dds.repo_add(http_repo.url) | tmp_project.dds.repo_add(http_repo.url) | ||||
tmp_project.dds.catalog_get('neo-sqlite3@0.3.0') | |||||
tmp_project.dds.pkg_get('neo-sqlite3@0.3.0') | |||||
assert tmp_project.root.joinpath('neo-sqlite3@0.3.0').is_dir() | assert tmp_project.root.joinpath('neo-sqlite3@0.3.0').is_dir() | ||||
assert tmp_project.root.joinpath('neo-sqlite3@0.3.0/package.jsonc').is_file() | assert tmp_project.root.joinpath('neo-sqlite3@0.3.0/package.jsonc').is_file() |
import subprocess | |||||
import pytest | |||||
import time | |||||
from dds_ci.testing import Project | |||||
def test_simple_compile_file(tmp_project: Project) -> None: | |||||
""" | |||||
Check that changing a source file will update the resulting application. | |||||
""" | |||||
with pytest.raises(subprocess.CalledProcessError): | |||||
tmp_project.compile_file('src/answer.cpp') | |||||
tmp_project.write('src/answer.cpp', 'int get_answer() { return 42; }') | |||||
# No error: | |||||
tmp_project.compile_file('src/answer.cpp') | |||||
# Fail: | |||||
time.sleep(0.5) | |||||
tmp_project.write('src/answer.cpp', 'int get_answer() { return "How many roads must a man walk down?"; }') | |||||
with pytest.raises(subprocess.CalledProcessError): | |||||
tmp_project.compile_file('src/answer.cpp') |
import pytest | import pytest | ||||
from pathlib import Path | |||||
from typing import Tuple | |||||
import subprocess | |||||
from dds_ci import proc | |||||
from dds_ci.testing import ProjectOpener, Project | from dds_ci.testing import ProjectOpener, Project | ||||
return project_opener.open('projects/sdist') | return project_opener.open('projects/sdist') | ||||
def test_create_sdist(test_project: Project) -> None: | |||||
def test_create_sdist(test_project: Project, tmp_path: Path) -> None: | |||||
# Create in the default location | |||||
test_project.sdist_create() | test_project.sdist_create() | ||||
sd_dir = test_project.build_root / 'foo@1.2.3.tar.gz' | sd_dir = test_project.build_root / 'foo@1.2.3.tar.gz' | ||||
assert sd_dir.is_file() | |||||
assert sd_dir.is_file(), 'Did not create an sdist in the default location' | |||||
# Create in a different location | |||||
dest = tmp_path / 'dummy.tar.gz' | |||||
test_project.sdist_create(dest=dest) | |||||
assert dest.is_file(), 'Did not create an sdist in the new location' | |||||
def test_export_sdist(test_project: Project) -> None: | |||||
test_project.sdist_export() | |||||
assert (test_project.dds.repo_dir / 'foo@1.2.3').is_dir() | |||||
def test_import_sdist_archive(test_project: Project) -> None: | |||||
@pytest.fixture() | |||||
def test_sdist(test_project: Project) -> Tuple[Path, Project]: | |||||
repo_content_path = test_project.dds.repo_dir / 'foo@1.2.3' | repo_content_path = test_project.dds.repo_dir / 'foo@1.2.3' | ||||
assert not repo_content_path.is_dir() | assert not repo_content_path.is_dir() | ||||
test_project.sdist_create() | test_project.sdist_create() | ||||
assert not repo_content_path.is_dir() | assert not repo_content_path.is_dir() | ||||
test_project.dds.repo_import(test_project.build_root / 'foo@1.2.3.tar.gz') | |||||
assert repo_content_path.is_dir() | |||||
assert repo_content_path.joinpath('library.jsonc').is_file() | |||||
return test_project.build_root / 'foo@1.2.3.tar.gz', test_project | |||||
def test_import_sdist_archive(test_sdist: Tuple[Path, Project]) -> None: | |||||
sdist, project = test_sdist | |||||
repo_content_path = project.dds.repo_dir / 'foo@1.2.3' | |||||
project.dds.pkg_import(sdist) | |||||
assert repo_content_path.is_dir(), \ | |||||
'The package did not appear in the local cache' | |||||
assert repo_content_path.joinpath('library.jsonc').is_file(), \ | |||||
'The package\'s library.jsonc did not get imported' | |||||
# Excluded file will not be in the sdist: | |||||
assert not repo_content_path.joinpath('other-file.txt').is_file(), \ | |||||
'Non-package content appeared in the package cache' | |||||
def test_import_sdist_stdin(test_sdist: Tuple[Path, Project]) -> None: | |||||
sdist, project = test_sdist | |||||
repo_content_path = project.dds.repo_dir / 'foo@1.2.3' | |||||
pipe = subprocess.Popen( | |||||
list(proc.flatten_cmd([ | |||||
project.dds.path, | |||||
project.dds.repo_dir_arg, | |||||
'pkg', | |||||
'import', | |||||
'--stdin', | |||||
])), | |||||
stdin=subprocess.PIPE, | |||||
) | |||||
assert pipe.stdin | |||||
with sdist.open('rb') as sdist_bin: | |||||
buf = sdist_bin.read(1024) | |||||
while buf: | |||||
pipe.stdin.write(buf) | |||||
buf = sdist_bin.read(1024) | |||||
pipe.stdin.close() | |||||
rc = pipe.wait() | |||||
assert rc == 0, 'Subprocess failed' | |||||
# project.dds.pkg_import(sdist) | |||||
assert repo_content_path.is_dir(), \ | |||||
'The package did not appear in the local cache' | |||||
assert repo_content_path.joinpath('library.jsonc').is_file(), \ | |||||
'The package\'s library.jsonc did not get imported' | |||||
# Excluded file will not be in the sdist: | # Excluded file will not be in the sdist: | ||||
assert not repo_content_path.joinpath('other-file.txt').is_file() | |||||
assert not repo_content_path.joinpath('other-file.txt').is_file(), \ | |||||
'Non-package content appeared in the package cache' |
import multiprocessing | import multiprocessing | ||||
import shutil | import shutil | ||||
from pathlib import Path | from pathlib import Path | ||||
from typing import Optional | |||||
import copy | |||||
from typing import Optional, TypeVar, Iterable | |||||
from . import paths, proc, toolchain as tc_mod | from . import paths, proc, toolchain as tc_mod | ||||
from dds_ci.util import Pathish | from dds_ci.util import Pathish | ||||
T = TypeVar('T') | |||||
class DDSWrapper: | class DDSWrapper: | ||||
""" | """ | ||||
self.catalog_path = Path(catalog_path or (self.repo_dir.parent / 'ci-catalog.db')) | self.catalog_path = Path(catalog_path or (self.repo_dir.parent / 'ci-catalog.db')) | ||||
self.default_cwd = default_cwd or Path.cwd() | self.default_cwd = default_cwd or Path.cwd() | ||||
def clone(self) -> 'DDSWrapper': | |||||
return DDSWrapper(self.path, | |||||
repo_dir=self.repo_dir, | |||||
catalog_path=self.catalog_path, | |||||
default_cwd=self.default_cwd) | |||||
def clone(self: T) -> T: | |||||
return copy.deepcopy(self) | |||||
@property | @property | ||||
def catalog_path_arg(self) -> str: | def catalog_path_arg(self) -> str: | ||||
"""The arguments for --repo-dir""" | """The arguments for --repo-dir""" | ||||
return f'--repo-dir={self.repo_dir}' | return f'--repo-dir={self.repo_dir}' | ||||
@property | |||||
def project_dir_flag(self) -> str: | |||||
return '--project-dir' | |||||
def set_repo_scratch(self, path: Pathish) -> None: | def set_repo_scratch(self, path: Pathish) -> None: | ||||
self.repo_dir = Path(path) / 'data' | self.repo_dir = Path(path) / 'data' | ||||
self.catalog_path = Path(path) / 'catalog.db' | self.catalog_path = Path(path) / 'catalog.db' | ||||
def catalog_get(self, what: str) -> None: | def catalog_get(self, what: str) -> None: | ||||
self.run(['catalog', 'get', self.catalog_path_arg, what]) | self.run(['catalog', 'get', self.catalog_path_arg, what]) | ||||
def pkg_get(self, what: str) -> None: | |||||
self.run(['pkg', 'get', self.catalog_path_arg, what]) | |||||
def repo_add(self, url: str) -> None: | def repo_add(self, url: str) -> None: | ||||
self.run(['repo', 'add', self.catalog_path_arg, url, '--update']) | |||||
self.run(['pkg', 'repo', 'add', self.catalog_path_arg, url]) | |||||
def repo_import(self, sdist: Path) -> None: | def repo_import(self, sdist: Path) -> None: | ||||
self.run(['repo', self.repo_dir_arg, 'import', sdist]) | self.run(['repo', self.repo_dir_arg, 'import', sdist]) | ||||
def pkg_import(self, filepath: Pathish) -> None: | |||||
self.run(['pkg', 'import', filepath, self.repo_dir_arg]) | |||||
def build(self, | def build(self, | ||||
*, | *, | ||||
root: Path, | root: Path, | ||||
self.repo_dir_arg, | self.repo_dir_arg, | ||||
self.catalog_path_arg, | self.catalog_path_arg, | ||||
f'--jobs={jobs}', | f'--jobs={jobs}', | ||||
f'--project-dir={root}', | |||||
f'{self.project_dir_flag}={root}', | |||||
f'--out={build_root}', | f'--out={build_root}', | ||||
]) | ]) | ||||
def compile_file(self, | |||||
paths: Iterable[Pathish], | |||||
*, | |||||
toolchain: Optional[Pathish] = None, | |||||
project_dir: Pathish, | |||||
out: Optional[Pathish] = None) -> None: | |||||
""" | |||||
Run 'dds compile-file' for the given paths. | |||||
""" | |||||
toolchain = toolchain or tc_mod.get_default_audit_toolchain() | |||||
self.run([ | |||||
'compile-file', | |||||
paths, | |||||
f'--toolchain={toolchain}', | |||||
f'{self.project_dir_flag}={project_dir}', | |||||
f'--out={out}', | |||||
]) | |||||
def build_deps(self, args: proc.CommandLine, *, toolchain: Optional[Path] = None) -> None: | def build_deps(self, args: proc.CommandLine, *, toolchain: Optional[Path] = None) -> None: | ||||
toolchain = toolchain or tc_mod.get_default_audit_toolchain() | toolchain = toolchain or tc_mod.get_default_audit_toolchain() | ||||
self.run([ | self.run([ | ||||
self.repo_dir_arg, | self.repo_dir_arg, | ||||
args, | args, | ||||
]) | ]) | ||||
class NewDDSWrapper(DDSWrapper): | |||||
""" | |||||
Wraps the new 'dds' executable with some convenience APIs | |||||
""" | |||||
@property | |||||
def repo_dir_arg(self) -> str: | |||||
return f'--pkg-cache-dir={self.repo_dir}' | |||||
@property | |||||
def catalog_path_arg(self) -> str: | |||||
return f'--pkg-db-path={self.catalog_path}' | |||||
@property | |||||
def project_dir_flag(self) -> str: | |||||
return '--project' |
from .util import Pathish | from .util import Pathish | ||||
CommandLineArg = Union[str, PurePath, int, float] | |||||
CommandLineArg = Union[str, Pathish, int, float] | |||||
CommandLineArg1 = Union[CommandLineArg, Iterable[CommandLineArg]] | CommandLineArg1 = Union[CommandLineArg, Iterable[CommandLineArg]] | ||||
CommandLineArg2 = Union[CommandLineArg1, Iterable[CommandLineArg1]] | CommandLineArg2 = Union[CommandLineArg1, Iterable[CommandLineArg1]] | ||||
CommandLineArg3 = Union[CommandLineArg2, Iterable[CommandLineArg2]] | CommandLineArg3 = Union[CommandLineArg2, Iterable[CommandLineArg2]] | ||||
def run(*cmd: CommandLine, cwd: Optional[Pathish] = None, check: bool = False) -> ProcessResult: | def run(*cmd: CommandLine, cwd: Optional[Pathish] = None, check: bool = False) -> ProcessResult: | ||||
return subprocess.run( | |||||
list(flatten_cmd(cmd)), | |||||
cwd=cwd, | |||||
check=check, | |||||
) | |||||
command = list(flatten_cmd(cmd)) | |||||
return subprocess.run(command, cwd=cwd, check=check) | |||||
def check_run(*cmd: CommandLine, cwd: Optional[Pathish] = None) -> ProcessResult: | def check_run(*cmd: CommandLine, cwd: Optional[Pathish] = None) -> ProcessResult: | ||||
return subprocess.run( | |||||
list(flatten_cmd(cmd)), | |||||
cwd=cwd, | |||||
check=True, | |||||
) | |||||
command = list(flatten_cmd(cmd)) | |||||
return subprocess.run(command, cwd=cwd, check=True) |
from _pytest.fixtures import FixtureRequest | from _pytest.fixtures import FixtureRequest | ||||
from dds_ci import toolchain, paths | from dds_ci import toolchain, paths | ||||
from ..dds import DDSWrapper | |||||
from ..dds import DDSWrapper, NewDDSWrapper | |||||
from ..util import Pathish | from ..util import Pathish | ||||
tc_mod = toolchain | tc_mod = toolchain | ||||
@property | @property | ||||
def project_dir_arg(self) -> str: | def project_dir_arg(self) -> str: | ||||
"""Argument for --project-dir""" | |||||
return f'--project-dir={self.root}' | |||||
"""Argument for --project""" | |||||
return f'--project={self.root}' | |||||
def build(self, *, toolchain: Optional[Pathish] = None) -> None: | def build(self, *, toolchain: Optional[Pathish] = None) -> None: | ||||
""" | """ | ||||
with tc_mod.fixup_toolchain(toolchain or tc_mod.get_default_test_toolchain()) as tc: | with tc_mod.fixup_toolchain(toolchain or tc_mod.get_default_test_toolchain()) as tc: | ||||
self.dds.build(root=self.root, build_root=self.build_root, toolchain=tc) | self.dds.build(root=self.root, build_root=self.build_root, toolchain=tc) | ||||
def sdist_create(self) -> None: | |||||
def compile_file(self, *paths: Pathish, toolchain: Optional[Pathish] = None) -> None: | |||||
with tc_mod.fixup_toolchain(toolchain or tc_mod.get_default_test_toolchain()) as tc: | |||||
self.dds.compile_file(paths, toolchain=tc, out=self.build_root, project_dir=self.root) | |||||
def sdist_create(self, *, dest: Optional[Pathish] = None) -> None: | |||||
self.build_root.mkdir(exist_ok=True, parents=True) | self.build_root.mkdir(exist_ok=True, parents=True) | ||||
self.dds.run(['sdist', 'create', self.project_dir_arg], cwd=self.build_root) | |||||
self.dds.run([ | |||||
'sdist', | |||||
'create', | |||||
self.project_dir_arg, | |||||
f'--out={dest}' if dest else (), | |||||
], cwd=self.build_root) | |||||
def sdist_export(self) -> None: | def sdist_export(self) -> None: | ||||
self.dds.run(['sdist', 'export', self.dds.repo_dir_arg, self.project_dir_arg]) | self.dds.run(['sdist', 'export', self.dds.repo_dir_arg, self.project_dir_arg]) | ||||
@pytest.fixture(scope='session') | @pytest.fixture(scope='session') | ||||
def dds_2(dds_exe: Path) -> DDSWrapper: | |||||
return DDSWrapper(dds_exe) | |||||
def dds_2(dds_exe: Path) -> NewDDSWrapper: | |||||
wr = NewDDSWrapper(dds_exe) | |||||
return wr | |||||
@pytest.fixture(scope='session') | @pytest.fixture(scope='session') | ||||
def dds_exe(pytestconfig: PyTestConfig) -> Path: | def dds_exe(pytestconfig: PyTestConfig) -> Path: | ||||
opt = pytestconfig.getoption('--dds-exe') or paths.CUR_BUILT_DDS | |||||
opt = pytestconfig.getoption('--dds-exe') or paths.BUILD_DIR / 'dds' | |||||
return Path(opt) | return Path(opt) |
def spec_as_local_tgz(dds_exe: Path, spec: SpecPackage) -> Iterator[Path]: | def spec_as_local_tgz(dds_exe: Path, spec: SpecPackage) -> Iterator[Path]: | ||||
with spec.remote.make_local_dir(spec.name, spec.version) as clone_dir: | with spec.remote.make_local_dir(spec.name, spec.version) as clone_dir: | ||||
out_tgz = clone_dir / 'sdist.tgz' | out_tgz = clone_dir / 'sdist.tgz' | ||||
check_call([str(dds_exe), 'sdist', 'create', f'--project-dir={clone_dir}', f'--out={out_tgz}']) | |||||
check_call([str(dds_exe), 'sdist', 'create', f'--project={clone_dir}', f'--out={out_tgz}']) | |||||
yield out_tgz | yield out_tgz | ||||