@@ -129,11 +129,13 @@ library_plan prepare_library(state& st, | |||
} | |||
} | |||
} | |||
return library_plan::create(lib, std::move(lp), pkg_man.namespace_ + "/" + lib.manifest().name); | |||
return library_plan::create(lib, | |||
std::move(lp), | |||
pkg_man.namespace_.str + "/" + lib.manifest().name.str); | |||
} | |||
package_plan prepare_one(state& st, const sdist_target& sd) { | |||
package_plan pkg{sd.sd.manifest.id.name, sd.sd.manifest.namespace_}; | |||
package_plan pkg{sd.sd.manifest.id.name.str, sd.sd.manifest.namespace_.str}; | |||
auto libs = collect_libraries(sd.sd.path); | |||
for (const auto& lib : libs) { | |||
pkg.add_library(prepare_library(st, sd, lib, sd.sd.manifest)); | |||
@@ -154,7 +156,7 @@ prepare_ureqs(const build_plan& plan, const toolchain& toolchain, path_ref out_r | |||
usage_requirement_map ureqs; | |||
for (const auto& pkg : plan.packages()) { | |||
for (const auto& lib : pkg.libraries()) { | |||
auto& lib_reqs = ureqs.add(pkg.namespace_(), lib.name()); | |||
auto& lib_reqs = ureqs.add(pkg.namespace_(), lib.name().str); | |||
lib_reqs.include_paths.push_back(lib.library_().public_include_dir()); | |||
lib_reqs.uses = lib.library_().manifest().uses; | |||
lib_reqs.links = lib.library_().manifest().links; | |||
@@ -174,7 +176,7 @@ void write_lml(build_env_ref env, const library_plan& lib, path_ref lml_path) { | |||
fs::create_directories(lml_path.parent_path()); | |||
auto out = open(lml_path, std::ios::binary | std::ios::out); | |||
out << "Type: Library\n" | |||
<< "Name: " << lib.name() << '\n' | |||
<< "Name: " << lib.name().str << '\n' | |||
<< "Include-Path: " << lib.library_().public_include_dir().generic_string() << '\n'; | |||
for (auto&& use : lib.uses()) { | |||
out << "Uses: " << use.namespace_ << "/" << use.name << '\n'; | |||
@@ -196,7 +198,7 @@ void write_lmp(build_env_ref env, const package_plan& pkg, path_ref lmp_path) { | |||
<< "Name: " << pkg.name() << '\n' | |||
<< "Namespace: " << pkg.namespace_() << '\n'; | |||
for (const auto& lib : pkg.libraries()) { | |||
auto lml_path = lmp_path.parent_path() / pkg.namespace_() / (lib.name() + ".lml"); | |||
auto lml_path = lmp_path.parent_path() / pkg.namespace_() / (lib.name().str + ".lml"); | |||
write_lml(env, lib, lml_path); | |||
out << "Library: " << lml_path.generic_string() << '\n'; | |||
} | |||
@@ -217,8 +219,8 @@ void write_lib_cmake(build_env_ref env, | |||
std::ostream& out, | |||
const package_plan& pkg, | |||
const library_plan& lib) { | |||
fmt::print(out, "# Library {}/{}\n", pkg.namespace_(), lib.name()); | |||
auto cmake_name = fmt::format("{}::{}", pkg.namespace_(), lib.name()); | |||
fmt::print(out, "# Library {}/{}\n", pkg.namespace_(), lib.name().str); | |||
auto cmake_name = fmt::format("{}::{}", pkg.namespace_(), lib.name().str); | |||
auto cm_kind = lib.archive_plan().has_value() ? "STATIC" : "INTERFACE"; | |||
fmt::print( | |||
out, |
@@ -36,7 +36,7 @@ library_plan library_plan::create(const library_root& lib, | |||
std::vector<source_file> lib_sources; | |||
std::vector<source_file> template_sources; | |||
auto qual_name = std::string(qual_name_.value_or(lib.manifest().name)); | |||
auto qual_name = std::string(qual_name_.value_or(lib.manifest().name.str)); | |||
// Collect the source for this library. This will look for any compilable sources in the | |||
// `src/` subdirectory of the library. | |||
@@ -84,7 +84,7 @@ library_plan library_plan::create(const library_root& lib, | |||
std::optional<create_archive_plan> archive_plan; | |||
if (!lib_compile_files.empty()) { | |||
dds_log(debug, "Generating an archive library for {}", qual_name); | |||
archive_plan.emplace(lib.manifest().name, | |||
archive_plan.emplace(lib.manifest().name.str, | |||
qual_name, | |||
params.out_subdir, | |||
std::move(lib_compile_files)); |
@@ -136,7 +136,7 @@ std::string render_template(std::string_view tmpl, const library_root& lib) { | |||
{ | |||
"lib", | |||
json_data::mapping_type{ | |||
{"name", lib.manifest().name}, | |||
{"name", lib.manifest().name.str}, | |||
{"root", lib.path().string()}, | |||
}, | |||
}, |
@@ -12,7 +12,7 @@ using namespace dds; | |||
namespace dds::cli::cmd { | |||
int build(const options& opts) { | |||
static int _build(const options& opts) { | |||
if (!opts.build.add_repos.empty()) { | |||
auto cat = opts.open_pkg_db(); | |||
for (auto& str : opts.build.add_repos) { | |||
@@ -38,4 +38,8 @@ int build(const options& opts) { | |||
return 0; | |||
} | |||
int build(const options& opts) { | |||
return handle_build_error([&] { return _build(opts); }); | |||
} | |||
} // namespace dds::cli::cmd |
@@ -1,9 +1,12 @@ | |||
#include "./build_common.hpp" | |||
#include <dds/error/errors.hpp> | |||
#include <dds/pkg/cache.hpp> | |||
#include <dds/pkg/db.hpp> | |||
#include <dds/pkg/get/get.hpp> | |||
#include <boost/leaf/handle_exception.hpp> | |||
using namespace dds; | |||
builder dds::cli::create_project_builder(const dds::cli::options& opts) { | |||
@@ -43,3 +46,15 @@ builder dds::cli::create_project_builder(const dds::cli::options& opts) { | |||
builder.add(sdist{std::move(man), opts.project_dir}, main_params); | |||
return builder; | |||
} | |||
int dds::cli::handle_build_error(std::function<int()> fn) { | |||
return boost::leaf::try_catch( // | |||
fn, | |||
[](user_error<errc::test_failure> exc) { | |||
write_error_marker("build-failed-test-failed"); | |||
dds_log(error, "{}", exc.what()); | |||
dds_log(error, "{}", exc.explanation()); | |||
dds_log(error, "Refer: {}", exc.error_reference()); | |||
return 1; | |||
}); | |||
} |
@@ -8,4 +8,6 @@ namespace dds::cli { | |||
dds::builder create_project_builder(const options& opts); | |||
int handle_build_error(std::function<int()>); | |||
} // namespace dds::cli |
@@ -1,5 +1,7 @@ | |||
#include "../options.hpp" | |||
#include "./build_common.hpp" | |||
#include <dds/build/builder.hpp> | |||
#include <dds/build/params.hpp> | |||
#include <dds/pkg/cache.hpp> | |||
@@ -12,7 +14,7 @@ | |||
namespace dds::cli::cmd { | |||
int build_deps(const options& opts) { | |||
static int _build_deps(const options& opts) { | |||
dds::build_params params{ | |||
.out_root = opts.out_path.value_or(fs::current_path() / "_deps"), | |||
.existing_lm_index = {}, | |||
@@ -62,4 +64,8 @@ int build_deps(const options& opts) { | |||
return 0; | |||
} | |||
int build_deps(const options& opts) { | |||
return handle_build_error([&] { return _build_deps(opts); }); | |||
} | |||
} // namespace dds::cli::cmd |
@@ -29,7 +29,7 @@ static int _pkg_ls(const options& opts) { | |||
}); | |||
for (const auto& [name, grp] : grp_by_name) { | |||
dds_log(info, "{}:", name); | |||
dds_log(info, "{}:", name.str); | |||
for (const dds::sdist& sd : grp) { | |||
dds_log(info, " - {}", sd.manifest.id.version.to_string()); | |||
} |
@@ -3,6 +3,8 @@ | |||
#include <dds/error/errors.hpp> | |||
#include <dds/error/toolchain.hpp> | |||
#include <dds/sdist/library/manifest.hpp> | |||
#include <dds/sdist/package.hpp> | |||
#include <dds/util/http/pool.hpp> | |||
#include <dds/util/log.hpp> | |||
#include <dds/util/result.hpp> | |||
@@ -42,13 +44,6 @@ auto handlers = std::tuple( // | |||
write_error_marker("package-json5-parse-error"); | |||
return 1; | |||
}, | |||
[](user_error<errc::test_failure> exc, matchv<cli::subcommand::build>) { | |||
write_error_marker("build-failed-test-failed"); | |||
dds_log(error, "{}", exc.what()); | |||
dds_log(error, "{}", exc.explanation()); | |||
dds_log(error, "Refer: {}", exc.error_reference()); | |||
return 1; | |||
}, | |||
[](boost::leaf::catch_<error_base> exc) { | |||
dds_log(error, "{}", exc.value().what()); | |||
dds_log(error, "{}", exc.value().explanation()); | |||
@@ -85,6 +80,67 @@ auto handlers = std::tuple( // | |||
} | |||
return 1; | |||
}, | |||
[](e_name_str badname, | |||
invalid_name_reason why, | |||
e_dependency_string depstr, | |||
e_package_manifest_path* pkman_path) { | |||
dds_log( | |||
error, | |||
"Invalid package name '.bold.red[{}]' in dependency string '.br.red[{}]': .br.yellow[{}]"_styled, | |||
badname.value, | |||
depstr.value, | |||
invalid_name_reason_str(why)); | |||
if (pkman_path) { | |||
dds_log(error, | |||
" (While reading package manifest from [.bold.yellow[{}]])"_styled, | |||
pkman_path->value); | |||
} | |||
write_error_marker("invalid-pkg-dep-name"); | |||
return 1; | |||
}, | |||
[](e_pkg_name_str, | |||
e_name_str badname, | |||
invalid_name_reason why, | |||
e_package_manifest_path* pkman_path) { | |||
dds_log(error, | |||
"Invalid package name '.bold.red[{}]': .br.yellow[{}]"_styled, | |||
badname.value, | |||
invalid_name_reason_str(why)); | |||
if (pkman_path) { | |||
dds_log(error, | |||
" (While reading package manifest from [.bold.yellow[{}]])"_styled, | |||
pkman_path->value); | |||
} | |||
write_error_marker("invalid-pkg-name"); | |||
return 1; | |||
}, | |||
[](e_pkg_namespace_str, | |||
e_name_str badname, | |||
invalid_name_reason why, | |||
e_package_manifest_path* pkman_path) { | |||
dds_log(error, | |||
"Invalid package namespace '.bold.red[{}]': .br.yellow[{}]"_styled, | |||
badname.value, | |||
invalid_name_reason_str(why)); | |||
if (pkman_path) { | |||
dds_log(error, | |||
" (While reading package manifest from [.bold.yellow[{}]])"_styled, | |||
pkman_path->value); | |||
} | |||
write_error_marker("invalid-pkg-namespace-name"); | |||
return 1; | |||
}, | |||
[](e_library_manifest_path libpath, invalid_name_reason why, e_name_str badname) { | |||
dds_log(error, | |||
"Invalid library name '.bold.red[{}]': .br.yellow[{}]"_styled, | |||
badname.value, | |||
invalid_name_reason_str(why)); | |||
dds_log(error, | |||
" (While reading library manifest from [.bold.yellow[{}]]"_styled, | |||
libpath.value); | |||
write_error_marker("invalid-lib-name"); | |||
return 1; | |||
}, | |||
[](e_system_error_exc exc, boost::leaf::verbose_diagnostic_info const& diag) { | |||
dds_log(critical, | |||
"An unhandled std::system_error arose. THIS IS A DDS BUG! Info: {}", |
@@ -61,7 +61,7 @@ void ensure_migrated(nsql::database& db) { | |||
PRAGMA foreign_keys = 1; | |||
DROP TABLE IF EXISTS dds_meta; | |||
CREATE TABLE IF NOT EXISTS dds_meta_1 AS | |||
WITH init (version) AS (VALUES ('eggs')) | |||
WITH init (version) AS (VALUES ('')) | |||
SELECT * FROM init; | |||
)"); | |||
nsql::transaction_guard tr{db}; |
@@ -1,8 +1,13 @@ | |||
#include "./deps.hpp" | |||
#include <dds/error/errors.hpp> | |||
#include <dds/error/on_error.hpp> | |||
#include <dds/error/result.hpp> | |||
#include <dds/util/log.hpp> | |||
#include <dds/util/string.hpp> | |||
#include <boost/leaf/exception.hpp> | |||
#include <ctre.hpp> | |||
#include <json5/parse_data.hpp> | |||
#include <semester/walk.hpp> | |||
@@ -14,21 +19,29 @@ | |||
using namespace dds; | |||
dependency dependency::parse_depends_string(std::string_view str) { | |||
DDS_E_SCOPE(e_dependency_string{std::string(str)}); | |||
auto sep_pos = str.find_first_of("=@^~+"); | |||
if (sep_pos == str.npos) { | |||
throw_user_error<errc::invalid_version_range_string>("Invalid dependency string '{}'", str); | |||
} | |||
auto name = str.substr(0, sep_pos); | |||
auto name = *dds::name::from_string(str.substr(0, sep_pos)); | |||
if (str[sep_pos] == '@') { | |||
++sep_pos; | |||
if (str[sep_pos] != '@') { | |||
static bool did_warn = false; | |||
if (!did_warn) { | |||
dds_log(warn, | |||
"Dependency version ranges are deprecated. All are treated as " | |||
"same-major-version. (Parsing dependency '{}')", | |||
str); | |||
} | |||
did_warn = true; | |||
} | |||
auto range_str = str.substr(sep_pos); | |||
auto range_str = "^" + std::string(str.substr(sep_pos + 1)); | |||
try { | |||
auto rng = semver::range::parse_restricted(range_str); | |||
return dependency{std::string(name), {rng.low(), rng.high()}}; | |||
return dependency{name, {rng.low(), rng.high()}}; | |||
} catch (const semver::invalid_range&) { | |||
throw_user_error<errc::invalid_version_range_string>( | |||
"Invalid version range string '{}' in dependency string '{}'", range_str, str); | |||
@@ -81,7 +94,7 @@ std::string iv_string(const pubgrub::interval_set<semver::version>::interval_typ | |||
std::string dependency::to_string() const noexcept { | |||
std::stringstream strm; | |||
strm << name << "@"; | |||
strm << name.str << "@"; | |||
if (versions.num_intervals() == 1) { | |||
auto iv = *versions.iter_intervals().begin(); | |||
if (iv.high == iv.low.next_after()) { | |||
@@ -89,7 +102,7 @@ std::string dependency::to_string() const noexcept { | |||
return strm.str(); | |||
} | |||
if (iv.low == semver::version() && iv.high == semver::version::max_version()) { | |||
return name; | |||
return name.str; | |||
} | |||
strm << "[" << iv_string(iv) << "]"; | |||
return strm.str(); |
@@ -1,5 +1,6 @@ | |||
#pragma once | |||
#include <dds/pkg/name.hpp> | |||
#include <dds/util/fs.hpp> | |||
#include <pubgrub/interval.hpp> | |||
@@ -12,8 +13,12 @@ namespace dds { | |||
using version_range_set = pubgrub::interval_set<semver::version>; | |||
struct e_dependency_string { | |||
std::string value; | |||
}; | |||
struct dependency { | |||
std::string name; | |||
dds::name name; | |||
version_range_set versions; | |||
static dependency parse_depends_string(std::string_view str); |
@@ -11,15 +11,14 @@ TEST_CASE("Parse dependency strings") { | |||
}; | |||
auto cur = GENERATE(Catch::Generators::values<case_>({ | |||
{"foo@1.2.3", "foo", "1.2.3", "1.2.4"}, | |||
{"foo=1.2.3", "foo", "1.2.3", "1.2.4"}, | |||
{"foo@1.2.3", "foo", "1.2.3", "2.0.0"}, | |||
{"foo=1.2.3", "foo", "1.2.3", "2.0.0"}, | |||
{"foo^1.2.3", "foo", "1.2.3", "2.0.0"}, | |||
{"foo~1.2.3", "foo", "1.2.3", "1.3.0"}, | |||
{"foo+1.2.3", "foo", "1.2.3", semver::version::max_version().to_string()}, | |||
{"foo~1.2.3", "foo", "1.2.3", "2.0.0"}, | |||
})); | |||
auto dep = dds::dependency::parse_depends_string(cur.depstr); | |||
CHECK(dep.name == cur.name); | |||
CHECK(dep.name.str == cur.name); | |||
CHECK(dep.versions.num_intervals() == 1); | |||
auto ver_iv = *dep.versions.iter_intervals().begin(); | |||
CHECK(ver_iv.low == semver::version::parse(cur.low)); |
@@ -0,0 +1,15 @@ | |||
#include "./handle.hpp" | |||
#include <dds/util/log.hpp> | |||
#include <boost/leaf/handle_exception.hpp> | |||
#include <boost/leaf/result.hpp> | |||
#include <fmt/ostream.h> | |||
using namespace dds; | |||
void dds::leaf_handle_unknown_void(std::string_view message, | |||
const boost::leaf::verbose_diagnostic_info& info) { | |||
dds_log(warn, message); | |||
dds_log(warn, "An unhandled error occurred:\n{}", info); | |||
} |
@@ -0,0 +1,26 @@ | |||
#pragma once | |||
#include <neo/fwd.hpp> | |||
#include <string_view> | |||
namespace boost::leaf { | |||
class verbose_diagnostic_info; | |||
} // namespace boost::leaf | |||
namespace dds { | |||
void leaf_handle_unknown_void(std::string_view message, | |||
const boost::leaf::verbose_diagnostic_info&); | |||
template <typename T> | |||
auto leaf_handle_unknown(std::string_view message, T&& val) { | |||
return [val = NEO_FWD(val), message](const boost::leaf::verbose_diagnostic_info& info) { | |||
leaf_handle_unknown_void(message, info); | |||
return val; | |||
}; | |||
} | |||
} // namespace dds |
@@ -1,5 +1,7 @@ | |||
#pragma once | |||
#include <neo/pp.hpp> | |||
#include <boost/leaf/on_error.hpp> | |||
/** |
@@ -9,4 +9,8 @@ namespace dds { | |||
using boost::leaf::new_error; | |||
struct e_human_message { | |||
std::string value; | |||
}; | |||
} // namespace dds |
@@ -1,13 +1,17 @@ | |||
#include "./cache.hpp" | |||
#include <dds/error/errors.hpp> | |||
#include <dds/error/handle.hpp> | |||
#include <dds/pkg/db.hpp> | |||
#include <dds/sdist/dist.hpp> | |||
#include <dds/sdist/library/manifest.hpp> | |||
#include <dds/solve/solve.hpp> | |||
#include <dds/util/log.hpp> | |||
#include <dds/util/paths.hpp> | |||
#include <dds/util/string.hpp> | |||
#include <boost/leaf/handle_exception.hpp> | |||
#include <fansi/styled.hpp> | |||
#include <neo/ref.hpp> | |||
#include <range/v3/action/sort.hpp> | |||
#include <range/v3/action/unique.hpp> | |||
@@ -17,7 +21,7 @@ | |||
#include <range/v3/view/transform.hpp> | |||
using namespace dds; | |||
using namespace fansi::literals; | |||
using namespace ranges; | |||
void pkg_cache::_log_blocking(path_ref dirpath) noexcept { | |||
@@ -29,28 +33,64 @@ void pkg_cache::_init_cache_dir(path_ref dirpath) noexcept { fs::create_director | |||
fs::path pkg_cache::default_local_path() noexcept { return dds_data_dir() / "pkg"; } | |||
pkg_cache pkg_cache::_open_for_directory(bool writeable, path_ref dirpath) { | |||
auto try_read_sdist = [](path_ref p) -> std::optional<sdist> { | |||
if (starts_with(p.filename().string(), ".")) { | |||
return std::nullopt; | |||
} | |||
try { | |||
return sdist::from_directory(p); | |||
} catch (const std::runtime_error& e) { | |||
dds_log(error, | |||
namespace { | |||
std::optional<sdist> try_open_sdist_for_directory(path_ref p) noexcept { | |||
if (starts_with(p.filename().string(), ".")) { | |||
return std::nullopt; | |||
} | |||
return boost::leaf::try_catch( // | |||
[&] { return std::make_optional(sdist::from_directory(p)); }, | |||
[&](boost::leaf::catch_<std::runtime_error> exc) { | |||
dds_log(warn, | |||
"Failed to load source distribution from directory '{}': {}", | |||
p.string(), | |||
e.what()); | |||
exc.value().what()); | |||
return std::nullopt; | |||
} | |||
}; | |||
}, | |||
[&](e_package_manifest_path*, | |||
e_library_manifest_path* lman_path, | |||
e_pkg_namespace_str* is_namespace, | |||
e_pkg_name_str* is_pkgname, | |||
e_name_str bad_name, | |||
invalid_name_reason why) { | |||
dds_log( | |||
warn, | |||
"Failed to load a source distribution contained in the package cache directory"); | |||
dds_log(warn, | |||
"The invalid source distribution is in [.bold.yellow[{}]]"_styled, | |||
p.string()); | |||
if (is_namespace) { | |||
dds_log(warn, | |||
"Invalid package namespace '.bold.yellow[{}]'"_styled, | |||
bad_name.value); | |||
} else if (is_pkgname) { | |||
dds_log(warn, "Invalid package name '.bold.yellow[{}]'"_styled, bad_name.value); | |||
} else if (lman_path) { | |||
dds_log( | |||
warn, | |||
"Invalid library name '.bold.yellow[{}]' (Defined in [.bold.yellow[{}]])"_styled, | |||
bad_name.value, | |||
lman_path->value); | |||
} | |||
dds_log(warn, " (.bold.yellow[{}])"_styled, invalid_name_reason_str(why)); | |||
dds_log(warn, "We will ignore this directory and not load it as an available package"); | |||
return std::nullopt; | |||
}, | |||
leaf_handle_unknown(fmt::format("Failed to load source distribution from directory [{}]", | |||
p.string()), | |||
std::nullopt)); | |||
} | |||
} // namespace | |||
pkg_cache pkg_cache::_open_for_directory(bool writeable, path_ref dirpath) { | |||
auto entries = | |||
// Get the top-level `name-version` dirs | |||
fs::directory_iterator(dirpath) // | |||
| neo::lref // | |||
// Convert each dir into an `sdist` object | |||
| ranges::views::transform(try_read_sdist) // | |||
| ranges::views::transform(try_open_sdist_for_directory) // | |||
// Drop items that failed to load | |||
| ranges::views::filter([](auto&& opt) { return opt.has_value(); }) // | |||
| ranges::views::transform([](auto&& opt) { return *opt; }) // | |||
@@ -126,7 +166,7 @@ std::vector<pkg_id> pkg_cache::solve(const std::vector<dependency>& deps, | |||
[&](std::string_view name) -> std::vector<pkg_id> { | |||
auto mine = ranges::views::all(_sdists) // | |||
| ranges::views::filter( | |||
[&](const sdist& sd) { return sd.manifest.id.name == name; }) | |||
[&](const sdist& sd) { return sd.manifest.id.name.str == name; }) | |||
| ranges::views::transform([](const sdist& sd) { return sd.manifest.id; }); | |||
auto avail = ctlg.by_name(name); | |||
auto all = ranges::views::concat(mine, avail) | ranges::to_vector; |
@@ -145,7 +145,7 @@ void migrate_repodb_3(nsql::database& db) { | |||
void do_store_pkg(neo::sqlite3::database& db, | |||
neo::sqlite3::statement_cache& st_cache, | |||
const pkg_listing& pkg) { | |||
dds_log(debug, "Recording package {}@{}", pkg.ident.name, pkg.ident.version.to_string()); | |||
dds_log(debug, "Recording package {}@{}", pkg.ident.name.str, pkg.ident.version.to_string()); | |||
auto& store_pkg_st = st_cache(R"( | |||
INSERT OR REPLACE INTO dds_pkgs | |||
(name, version, remote_url, description) | |||
@@ -153,7 +153,7 @@ void do_store_pkg(neo::sqlite3::database& db, | |||
(?, ?, ?, ?) | |||
)"_sql); | |||
nsql::exec(store_pkg_st, | |||
pkg.ident.name, | |||
pkg.ident.name.str, | |||
pkg.ident.version.to_string(), | |||
pkg.remote_pkg.to_url_string(), | |||
pkg.description); | |||
@@ -177,7 +177,11 @@ void do_store_pkg(neo::sqlite3::database& db, | |||
assert(dep.versions.num_intervals() == 1); | |||
auto iv_1 = *dep.versions.iter_intervals().begin(); | |||
dds_log(trace, " Depends on: {}", dep.to_string()); | |||
nsql::exec(new_dep_st, db_pkg_id, dep.name, iv_1.low.to_string(), iv_1.high.to_string()); | |||
nsql::exec(new_dep_st, | |||
db_pkg_id, | |||
dep.name.str, | |||
iv_1.low.to_string(), | |||
iv_1.high.to_string()); | |||
} | |||
} | |||
@@ -275,7 +279,7 @@ void pkg_db::store(const pkg_listing& pkg) { | |||
result<pkg_listing> pkg_db::get(const pkg_id& pk_id) const noexcept { | |||
auto ver_str = pk_id.version.to_string(); | |||
dds_log(trace, "Lookup package {}@{}", pk_id.name, ver_str); | |||
dds_log(trace, "Lookup package {}@{}", pk_id.name.str, ver_str); | |||
auto& st = _stmt_cache(R"( | |||
SELECT | |||
pkg_id, | |||
@@ -288,7 +292,7 @@ result<pkg_listing> pkg_db::get(const pkg_id& pk_id) const noexcept { | |||
ORDER BY pkg_id DESC | |||
)"_sql); | |||
st.reset(); | |||
st.bindings() = std::forward_as_tuple(pk_id.name, ver_str); | |||
st.bindings() = std::forward_as_tuple(pk_id.name.str, ver_str); | |||
auto ec = st.step(std::nothrow); | |||
if (ec == nsql::errc::done) { | |||
return new_error([&] { | |||
@@ -317,7 +321,7 @@ result<pkg_listing> pkg_db::get(const pkg_id& pk_id) const noexcept { | |||
} | |||
neo_assert(invariant, | |||
pk_id.name == name && pk_id.version == semver::version::parse(version), | |||
pk_id.name.str == name && pk_id.version == semver::version::parse(version), | |||
"Package metadata does not match", | |||
pk_id.to_string(), | |||
name, | |||
@@ -364,7 +368,7 @@ std::vector<pkg_id> pkg_db::by_name(std::string_view sv) const noexcept { | |||
} | |||
std::vector<dependency> pkg_db::dependencies_of(const pkg_id& pkg) const noexcept { | |||
dds_log(trace, "Lookup dependencies of {}@{}", pkg.name, pkg.version.to_string()); | |||
dds_log(trace, "Lookup dependencies of {}", pkg.to_string()); | |||
return nsql::exec_tuples<std::string, | |||
std::string, | |||
std::string>( // | |||
@@ -380,7 +384,7 @@ std::vector<dependency> pkg_db::dependencies_of(const pkg_id& pkg) const noexcep | |||
WHERE pkg_id IN this_pkg_id | |||
ORDER BY dep_name | |||
)"_sql), | |||
pkg.name, | |||
pkg.name.str, | |||
pkg.version.to_string()) // | |||
| neo::lref // | |||
| ranges::views::transform([](auto&& pair) { |
@@ -34,7 +34,7 @@ TEST_CASE_METHOD(pkg_db_test_case, "Store a simple package") { | |||
auto pkgs = db.by_name("foo"); | |||
REQUIRE(pkgs.size() == 1); | |||
CHECK(pkgs[0].name == "foo"); | |||
CHECK(pkgs[0].name.str == "foo"); | |||
CHECK(pkgs[0].version == semver::version::parse("1.2.3")); | |||
auto info = db.get(pkgs[0]); | |||
REQUIRE(info); | |||
@@ -67,9 +67,9 @@ TEST_CASE_METHOD(pkg_db_test_case, "Package requirements") { | |||
}); | |||
auto pkgs = db.by_name("foo"); | |||
REQUIRE(pkgs.size() == 1); | |||
CHECK(pkgs[0].name == "foo"); | |||
CHECK(pkgs[0].name.str == "foo"); | |||
auto deps = db.dependencies_of(pkgs[0]); | |||
CHECK(deps.size() == 2); | |||
CHECK(deps[0].name == "bar"); | |||
CHECK(deps[1].name == "baz"); | |||
CHECK(deps[0].name.str == "bar"); | |||
CHECK(deps[1].name.str == "baz"); | |||
} |
@@ -32,7 +32,7 @@ dds_http_remote_pkg dds_http_remote_pkg::from_url(const neo::url& url) { | |||
void dds_http_remote_pkg::do_get_raw(path_ref dest) const { | |||
auto http_url = repo_url; | |||
fs::path path = fs::path(repo_url.path) / "pkg" / pkg_id.name / pkg_id.version.to_string() | |||
fs::path path = fs::path(repo_url.path) / "pkg" / pkg_id.name.str / pkg_id.version.to_string() | |||
/ "sdist.tar.gz"; | |||
http_url.path = path.lexically_normal().generic_string(); | |||
http_remote_pkg http; |
@@ -6,7 +6,7 @@ TEST_CASE("Parse a URL") { | |||
auto pkg = dds::dds_http_remote_pkg::from_url( | |||
neo::url::parse("dds+http://foo.bar/repo-dir/egg@1.2.3")); | |||
CHECK(pkg.repo_url.to_string() == "http://foo.bar/repo-dir"); | |||
CHECK(pkg.pkg_id.name == "egg"); | |||
CHECK(pkg.pkg_id.name.str == "egg"); | |||
CHECK(pkg.pkg_id.version.to_string() == "1.2.3"); | |||
CHECK(pkg.to_url_string() == "dds+http://foo.bar/repo-dir/egg@1.2.3"); | |||
} |
@@ -10,21 +10,23 @@ | |||
using namespace dds; | |||
pkg_id pkg_id::parse(const std::string_view s) { | |||
DDS_E_SCOPE(e_invalid_pkg_id_str{std::string(s)}); | |||
DDS_E_SCOPE(e_pkg_id_str{std::string(s)}); | |||
auto at_pos = s.find('@'); | |||
if (at_pos == s.npos) { | |||
BOOST_LEAF_THROW_EXCEPTION( | |||
make_user_error<errc::invalid_pkg_id>("Package ID must contain an '@' symbol")); | |||
} | |||
auto name = s.substr(0, at_pos); | |||
auto name = *dds::name::from_string(s.substr(0, at_pos)); | |||
auto ver_str = s.substr(at_pos + 1); | |||
try { | |||
return {std::string(name), semver::version::parse(ver_str)}; | |||
return {name, semver::version::parse(ver_str)}; | |||
} catch (const semver::invalid_version& err) { | |||
BOOST_LEAF_THROW_EXCEPTION(make_user_error<errc::invalid_pkg_id>(), err); | |||
} | |||
} | |||
std::string pkg_id::to_string() const noexcept { return name + "@" + version.to_string(); } | |||
std::string pkg_id::to_string() const noexcept { | |||
return fmt::format("{}@{}", name.str, version.to_string()); | |||
} |
@@ -1,5 +1,7 @@ | |||
#pragma once | |||
#include "./name.hpp" | |||
#include <semver/version.hpp> | |||
#include <string> | |||
@@ -8,7 +10,7 @@ | |||
namespace dds { | |||
struct e_invalid_pkg_id_str { | |||
struct e_pkg_id_str { | |||
std::string value; | |||
}; | |||
@@ -21,7 +23,7 @@ struct e_invalid_pkg_id_str { | |||
*/ | |||
struct pkg_id { | |||
/// The name of the package | |||
std::string name; | |||
dds::name name; | |||
/// The version of the package | |||
semver::version version; | |||
@@ -16,7 +16,7 @@ TEST_CASE("Package package ID strings") { | |||
auto pk_id = dds::pkg_id::parse(id_str); | |||
CHECK(pk_id.to_string() == id_str); | |||
CHECK(pk_id.name == exp_name); | |||
CHECK(pk_id.name.str == exp_name); | |||
CHECK(pk_id.version.to_string() == exp_ver); | |||
} | |||
@@ -0,0 +1,73 @@ | |||
#include "./name.hpp" | |||
#include <dds/error/on_error.hpp> | |||
#include <dds/error/result.hpp> | |||
#include <neo/assert.hpp> | |||
#include <ctre.hpp> | |||
using namespace dds; | |||
using err_reason = invalid_name_reason; | |||
static err_reason calc_invalid_name_reason(std::string_view str) noexcept { | |||
constexpr ctll::fixed_string capital_re = "[A-Z]"; | |||
constexpr ctll::fixed_string double_punct = "[._\\-]{2}"; | |||
constexpr ctll::fixed_string end_punct = "[._\\-]$"; | |||
constexpr ctll::fixed_string ws = "\\s"; | |||
constexpr ctll::fixed_string invalid_chars = "[^a-z0-9._\\-]"; | |||
if (str.empty()) { | |||
return err_reason::empty; | |||
} else if (ctre::search<capital_re>(str)) { | |||
return err_reason::capital; | |||
} else if (ctre::search<double_punct>(str)) { | |||
return err_reason::double_punct; | |||
} else if (str[0] < 'a' || str[0] > 'z') { | |||
return err_reason::initial_not_alpha; | |||
} else if (ctre::search<end_punct>(str)) { | |||
return err_reason::end_punct; | |||
} else if (ctre::search<ws>(str)) { | |||
return err_reason::whitespace; | |||
} else if (ctre::search<invalid_chars>(str)) { | |||
return err_reason::invalid_char; | |||
} else { | |||
neo_assert(invariant, | |||
false, | |||
"Expected to be able to determine an error-reason for the given invalid name", | |||
str); | |||
} | |||
} | |||
std::string_view dds::invalid_name_reason_str(err_reason e) noexcept { | |||
switch (e) { | |||
case err_reason::capital: | |||
return "Uppercase letters are not valid in package names"; | |||
case err_reason::double_punct: | |||
return "Adjacent punctuation characters are not valid in package names"; | |||
case err_reason::end_punct: | |||
return "Names must not end with a punctuation character"; | |||
case err_reason::whitespace: | |||
return "Names must not contain whitespace"; | |||
case err_reason::invalid_char: | |||
return "Name contains an invalid character"; | |||
case err_reason::initial_not_alpha: | |||
return "Name must begin with a lowercase alphabetic character"; | |||
case err_reason::empty: | |||
return "Name cannot be empty"; | |||
} | |||
neo::unreachable(); | |||
} | |||
result<name> name::from_string(std::string_view str) noexcept { | |||
constexpr ctll::fixed_string name_re = "^([a-z][a-z0-9]*)([._\\-][a-z0-9]+)*$"; | |||
auto mat = ctre::match<name_re>(str); | |||
if (!mat) { | |||
return new_error(DDS_E_ARG(e_name_str{std::string(str)}), | |||
DDS_E_ARG(calc_invalid_name_reason(str))); | |||
} | |||
return name{std::string(str)}; | |||
} |
@@ -0,0 +1,36 @@ | |||
#pragma once | |||
#include <dds/error/result_fwd.hpp> | |||
#include <string> | |||
namespace dds { | |||
enum class invalid_name_reason { | |||
empty, | |||
capital, | |||
initial_not_alpha, | |||
double_punct, | |||
end_punct, | |||
invalid_char, | |||
whitespace, | |||
}; | |||
struct e_name_str { | |||
std::string value; | |||
}; | |||
std::string_view invalid_name_reason_str(invalid_name_reason) noexcept; | |||
struct name { | |||
std::string str; | |||
// Parse a package name, ensuring it is a valid package name string | |||
static result<name> from_string(std::string_view str) noexcept; | |||
bool operator==(const name& o) const noexcept { return str == o.str; } | |||
bool operator!=(const name& o) const noexcept { return str != o.str; } | |||
bool operator<(const name& o) const noexcept { return str < o.str; } | |||
}; | |||
} // namespace dds |
@@ -0,0 +1,72 @@ | |||
#include "./name.hpp" | |||
#include <dds/error/result.hpp> | |||
#include <boost/leaf/handle_error.hpp> | |||
#include <catch2/catch.hpp> | |||
TEST_CASE("Try some invalid names") { | |||
using reason = dds::invalid_name_reason; | |||
struct case_ { | |||
std::string_view invalid_name; | |||
dds::invalid_name_reason error; | |||
}; | |||
auto given = GENERATE(Catch::Generators::values<case_>({ | |||
{"", reason::empty}, | |||
{"H", reason::capital}, | |||
{"heLlo", reason::capital}, | |||
{"eGGG", reason::capital}, | |||
{"e0131F-gg", reason::capital}, | |||
{"-foo", reason::initial_not_alpha}, | |||
{"123", reason::initial_not_alpha}, | |||
{"123-bar", reason::initial_not_alpha}, | |||
{" fooo", reason::initial_not_alpha}, | |||
{"foo..bar", reason::double_punct}, | |||
{"foo-.bar", reason::double_punct}, | |||
{"foo-_bar", reason::double_punct}, | |||
{"foo__bar", reason::double_punct}, | |||
{"foo_.bar", reason::double_punct}, | |||
{"foo.", reason::end_punct}, | |||
{"foo.bar_", reason::end_punct}, | |||
{"foo.bar-", reason::end_punct}, | |||
{"foo ", reason::whitespace}, | |||
{"foo bar", reason::whitespace}, | |||
{"foo\nbar", reason::whitespace}, | |||
{"foo&bar", reason::invalid_char}, | |||
{"foo+baz", reason::invalid_char}, | |||
})); | |||
boost::leaf::context<reason> err_ctx; | |||
err_ctx.activate(); | |||
CAPTURE(given.invalid_name); | |||
auto res = dds::name::from_string(given.invalid_name); | |||
err_ctx.deactivate(); | |||
CHECKED_IF(!res) { | |||
err_ctx.handle_error<void>( | |||
res.error(), | |||
[&](reason r) { CHECK(r == given.error); }, | |||
[] { FAIL_CHECK("No error reason was given"); }); | |||
} | |||
} | |||
TEST_CASE("Try some valid names") { | |||
auto given = GENERATE(Catch::Generators::values<std::string_view>({ | |||
"hi", | |||
"dog", | |||
"foo.bar", | |||
"foo-bar_bark", | |||
"foo-bar-baz", | |||
"foo-bar.quz", | |||
"q", | |||
})); | |||
auto res = dds::name::from_string(given); | |||
CHECK(res->str == given); | |||
} |
@@ -149,8 +149,7 @@ void repo_manager::import_targz(path_ref tgz_file) { | |||
DDS_E_SCOPE(man->id); | |||
neo::sqlite3::transaction_guard tr{_db}; | |||
dds_log(debug, "Recording package {}@{}", man->id.name, man->id.version.to_string()); | |||
dds_log(debug, "Recording package {}", man->id.to_string()); | |||
dds::pkg_listing info{.ident = man->id, | |||
.deps = man->dependencies, | |||
.description = "[No description]", | |||
@@ -158,7 +157,7 @@ void repo_manager::import_targz(path_ref tgz_file) { | |||
auto rel_url = fmt::format("dds:{}", man->id.to_string()); | |||
add_pkg(info, rel_url); | |||
auto dest_path = pkg_dir() / man->id.name / man->id.version.to_string() / "sdist.tar.gz"; | |||
auto dest_path = pkg_dir() / man->id.name.str / man->id.version.to_string() / "sdist.tar.gz"; | |||
fs::create_directories(dest_path.parent_path()); | |||
fs::copy(tgz_file, dest_path); | |||
@@ -176,11 +175,11 @@ void repo_manager::delete_package(pkg_id pkg_id) { | |||
WHERE name = ? | |||
AND version = ? | |||
)"_sql), | |||
pkg_id.name, | |||
pkg_id.name.str, | |||
pkg_id.version.to_string()); | |||
/// XXX: Verify with _db.changes() that we actually deleted one row | |||
auto name_dir = pkg_dir() / pkg_id.name; | |||
auto name_dir = pkg_dir() / pkg_id.name.str; | |||
auto ver_dir = name_dir / pkg_id.version.to_string(); | |||
DDS_E_SCOPE(e_repo_delete_path{ver_dir}); | |||
@@ -210,7 +209,7 @@ void repo_manager::add_pkg(const pkg_listing& info, std::string_view url) { | |||
INSERT INTO dds_repo_packages (name, version, description, url) | |||
VALUES (?, ?, ?, ?) | |||
)"_sql), | |||
info.ident.name, | |||
info.ident.name.str, | |||
info.ident.version.to_string(), | |||
info.description, | |||
url); | |||
@@ -227,12 +226,12 @@ void repo_manager::add_pkg(const pkg_listing& info, std::string_view url) { | |||
dds_log(trace, " Depends on: {}", dep.to_string()); | |||
nsql::exec(insert_dep_st, | |||
package_rowid, | |||
dep.name, | |||
dep.name.str, | |||
iv_1.low.to_string(), | |||
iv_1.high.to_string()); | |||
} | |||
auto dest_dir = pkg_dir() / info.ident.name / info.ident.version.to_string(); | |||
auto dest_dir = pkg_dir() / info.ident.name.str / info.ident.version.to_string(); | |||
auto stamp_path = dest_dir / "url.txt"; | |||
fs::create_directories(dest_dir); | |||
std::ofstream stamp_file{stamp_path, std::ios::binary}; |
@@ -2,6 +2,8 @@ | |||
#include <dds/dym.hpp> | |||
#include <dds/error/errors.hpp> | |||
#include <dds/error/on_error.hpp> | |||
#include <dds/error/result.hpp> | |||
#include <dds/util/algo.hpp> | |||
#include <json5/parse_data.hpp> | |||
@@ -11,6 +13,8 @@ | |||
using namespace dds; | |||
library_manifest library_manifest::load_from_file(path_ref fpath) { | |||
DDS_E_SCOPE(e_library_manifest_path{fpath.string()}); | |||
auto content = slurp_file(fpath); | |||
auto data = json5::parse_data(content); | |||
@@ -27,7 +31,7 @@ library_manifest library_manifest::load_from_file(path_ref fpath) { | |||
mapping{ | |||
if_key{"name", | |||
require_type<std::string>{"`name` must be a string"}, | |||
put_into{lib.name}}, | |||
put_into{lib.name.str}}, | |||
if_key{"uses", | |||
require_type<json5::data::array_type>{ | |||
"`uses` must be an array of usage requirements"}, | |||
@@ -54,10 +58,11 @@ library_manifest library_manifest::load_from_file(path_ref fpath) { | |||
throw_user_error<errc::invalid_lib_manifest>(rej->message); | |||
} | |||
if (lib.name.empty()) { | |||
if (lib.name.str.empty()) { | |||
throw_user_error<errc::invalid_lib_manifest>( | |||
"The 'name' field is required (Reading library manifest [{}])", fpath.string()); | |||
} | |||
lib.name = *dds::name::from_string(lib.name.str); | |||
return lib; | |||
} |
@@ -1,5 +1,6 @@ | |||
#pragma once | |||
#include <dds/pkg/name.hpp> | |||
#include <dds/util/fs.hpp> | |||
#include <libman/library.hpp> | |||
@@ -8,6 +9,10 @@ | |||
namespace dds { | |||
struct e_library_manifest_path { | |||
std::string value; | |||
}; | |||
/** | |||
* Represents the contents of a `library.json5`. This is somewhat a stripped-down | |||
* version of lm::library, to only represent exactly the parts that we want to | |||
@@ -15,7 +20,7 @@ namespace dds { | |||
*/ | |||
struct library_manifest { | |||
/// The name of the library | |||
std::string name; | |||
dds::name name; | |||
/// The libraries that the owning library "uses" | |||
std::vector<lm::usage> uses; | |||
/// The libraries that the owning library must be linked with |
@@ -2,6 +2,7 @@ | |||
#include <dds/build/plan/compile_file.hpp> | |||
#include <dds/error/errors.hpp> | |||
#include <dds/error/result.hpp> | |||
#include <dds/sdist/root.hpp> | |||
#include <dds/util/algo.hpp> | |||
#include <dds/util/log.hpp> | |||
@@ -60,10 +61,16 @@ library_root library_root::from_directory(path_ref lib_dir) { | |||
auto sources = collect_pf_sources(lib_dir); | |||
library_manifest man; | |||
man.name = lib_dir.filename().string(); | |||
auto found = library_manifest::find_in_directory(lib_dir); | |||
auto found = library_manifest::find_in_directory(lib_dir); | |||
if (found) { | |||
man = library_manifest::load_from_file(*found); | |||
} else { | |||
auto name_from_dir = dds::name::from_string(lib_dir.filename().string()); | |||
if (!name_from_dir) { | |||
man.name.str = "unnamed"; | |||
} else { | |||
man.name = *name_from_dir; | |||
} | |||
} | |||
auto lib = library_root(lib_dir, std::move(sources), std::move(man)); |
@@ -52,11 +52,19 @@ package_manifest parse_json(const json5::data& data, std::string_view fpath) { | |||
required_key{"name", | |||
"A string 'name' is required", | |||
require_str{"'name' must be a string"}, | |||
put_into{ret.id.name}}, | |||
put_into{ret.id.name, | |||
[](std::string s) { | |||
DDS_E_SCOPE(e_pkg_name_str{s}); | |||
return *dds::name::from_string(s); | |||
}}}, | |||
required_key{"namespace", | |||
"A string 'namespace' is a required ", | |||
require_str{"'namespace' must be a string"}, | |||
put_into{ret.namespace_}}, | |||
put_into{ret.namespace_, | |||
[](std::string s) { | |||
DDS_E_SCOPE(e_pkg_namespace_str{s}); | |||
return *dds::name::from_string(s); | |||
}}}, | |||
required_key{"version", | |||
"A 'version' string is requried", | |||
require_str{"'version' must be a string"}, | |||
@@ -104,12 +112,14 @@ package_manifest parse_json(const json5::data& data, std::string_view fpath) { | |||
} // namespace | |||
package_manifest package_manifest::load_from_file(const fs::path& fpath) { | |||
DDS_E_SCOPE(e_package_manifest_path{fpath.string()}); | |||
auto content = slurp_file(fpath); | |||
return load_from_json5_str(content, fpath.string()); | |||
} | |||
package_manifest package_manifest::load_from_json5_str(std::string_view content, | |||
std::string_view input_name) { | |||
DDS_E_SCOPE(e_package_manifest_path{std::string(input_name)}); | |||
try { | |||
auto data = json5::parse_data(content); | |||
return parse_json(data, input_name); |
@@ -19,14 +19,26 @@ enum class test_lib { | |||
catch_main, | |||
}; | |||
struct e_package_manifest_path { | |||
std::string value; | |||
}; | |||
struct e_pkg_name_str { | |||
std::string value; | |||
}; | |||
struct e_pkg_namespace_str { | |||
std::string value; | |||
}; | |||
/** | |||
* Struct representing the contents of a `packaeg.dds` file. | |||
* Struct representing the contents of a `package.json5` file. | |||
*/ | |||
struct package_manifest { | |||
/// The package ID, as determined by `Name` and `Version` together | |||
dds::pkg_id id; | |||
/// The declared `Namespace` of the package. This directly corresponds with the libman Namespace | |||
std::string namespace_; | |||
name namespace_; | |||
/// The `test_driver` that this package declares, or `nullopt` if absent. | |||
std::optional<test_lib> test_driver; | |||
/// The dependencies declared with the `Depends` fields, if any. |
@@ -66,7 +66,7 @@ struct req_type { | |||
auto as_pkg_id(const req_type& req) { | |||
const version_range_set& versions = req.dep.versions; | |||
assert(versions.num_intervals() == 1); | |||
return pkg_id{req.dep.name, (*versions.iter_intervals().begin()).low}; | |||
return pkg_id{req.dep.name.str, (*versions.iter_intervals().begin()).low}; | |||
} | |||
struct solver_provider { | |||
@@ -78,10 +78,10 @@ struct solver_provider { | |||
std::optional<req_type> best_candidate(const req_type& req) const { | |||
dds_log(debug, "Find best candidate of {}", req.dep.to_string()); | |||
// Look up in the cachce for the packages we have with the given name | |||
auto found = pkgs_by_name.find(req.dep.name); | |||
auto found = pkgs_by_name.find(req.dep.name.str); | |||
if (found == pkgs_by_name.end()) { | |||
// If it isn't there, insert an entry in the cache | |||
found = pkgs_by_name.emplace(req.dep.name, pkgs_for_name(req.dep.name)).first; | |||
found = pkgs_by_name.emplace(req.dep.name.str, pkgs_for_name(req.dep.name.str)).first; | |||
} | |||
// Find the first package with the version contained by the ranges in the requirement | |||
auto& for_name = found->second; | |||
@@ -99,7 +99,7 @@ struct solver_provider { | |||
std::vector<req_type> requirements_of(const req_type& req) const { | |||
dds_log(trace, | |||
"Lookup requirements of {}@{}", | |||
req.key(), | |||
req.key().str, | |||
(*req.dep.versions.iter_intervals().begin()).low.to_string()); | |||
auto pk_id = as_pkg_id(req); | |||
auto deps = deps_for_pkg(pk_id); |
@@ -50,10 +50,6 @@ struct e_url_string { | |||
std::string value; | |||
}; | |||
struct e_human_message { | |||
std::string value; | |||
}; | |||
struct e_missing_file { | |||
std::filesystem::path path; | |||
}; |
@@ -45,14 +45,14 @@ def test_simple_lib(tmp_project: Project) -> None: | |||
""" | |||
tmp_project.write('src/foo.cpp', 'int the_answer() { return 42; }') | |||
tmp_project.package_json = { | |||
'name': 'TestProject', | |||
'name': 'test-project', | |||
'version': '0.0.0', | |||
'namespace': 'test', | |||
} | |||
tmp_project.library_json = {'name': 'TestLibrary'} | |||
tmp_project.library_json = {'name': 'test-library'} | |||
tmp_project.build() | |||
assert (tmp_project.build_root / 'compile_commands.json').is_file() | |||
assert list(tmp_project.build_root.glob('libTestLibrary.*')) != [] | |||
assert (tmp_project.build_root / 'compile_commands.json').is_file(), 'compdb was not created' | |||
assert list(tmp_project.build_root.glob('libtest-library.*')) != [], 'No archive was created' | |||
def test_lib_with_just_test(tmp_project: Project) -> None: | |||
@@ -67,6 +67,53 @@ def test_lib_with_failing_test(tmp_project: Project) -> None: | |||
tmp_project.build() | |||
def test_invalid_names(tmp_project: Project) -> None: | |||
tmp_project.package_json = { | |||
'name': 'test', | |||
'version': '1.2.3', | |||
'namespace': 'test', | |||
'depends': ['invalid name@1.2.3'] | |||
} | |||
with expect_error_marker('invalid-pkg-dep-name'): | |||
tmp_project.build() | |||
with expect_error_marker('invalid-pkg-dep-name'): | |||
tmp_project.pkg_create() | |||
with expect_error_marker('invalid-pkg-dep-name'): | |||
tmp_project.dds.build_deps(['invalid name@1.2.3']) | |||
tmp_project.package_json = { | |||
**tmp_project.package_json, | |||
'name': 'invalid name', | |||
'depends': [], | |||
} | |||
with expect_error_marker('invalid-pkg-name'): | |||
tmp_project.build() | |||
with expect_error_marker('invalid-pkg-name'): | |||
tmp_project.pkg_create() | |||
tmp_project.package_json = { | |||
**tmp_project.package_json, | |||
'name': 'simple_name', | |||
'namespace': 'invalid namespace', | |||
} | |||
with expect_error_marker('invalid-pkg-namespace-name'): | |||
tmp_project.build() | |||
with expect_error_marker('invalid-pkg-namespace-name'): | |||
tmp_project.pkg_create() | |||
tmp_project.package_json = { | |||
'name': 'test', | |||
'version': '1.2.3', | |||
'namespace': 'test', | |||
'depends': [], | |||
} | |||
tmp_project.library_json = {'name': 'invalid name'} | |||
# Need a source directory for dds to load the lib manifest | |||
tmp_project.write('src/empty.hpp', '') | |||
with expect_error_marker('invalid-lib-name'): | |||
tmp_project.build() | |||
TEST_PACKAGE: PackageJSON = { | |||
'name': 'test-pkg', | |||
'version': '0.2.2', |
@@ -1,3 +1,5 @@ | |||
import json | |||
from dds_ci.dds import DDSWrapper | |||
from dds_ci.testing import Project, RepoServer, PackageJSON | |||
from dds_ci.testing.error import expect_error_marker | |||
@@ -70,3 +72,30 @@ def test_pkg_search(_test_repo: RepoServer, tmp_project: Project) -> None: | |||
dds.run(['pkg', dds.pkg_db_path_arg, 'search', 'neo-*']) | |||
with expect_error_marker('pkg-search-no-result'): | |||
dds.run(['pkg', dds.pkg_db_path_arg, 'search', 'nonexistent']) | |||
def test_pkg_cache_invalid_nofail(tmp_project: Project) -> None: | |||
""" | |||
Check that dds will not fail a build just because the package cache has an invalid | |||
object within. | |||
""" | |||
sdist_dir = tmp_project.dds.repo_dir / 'bad@1.2.3' | |||
sdist_dir.mkdir(parents=True) | |||
tmp_project.build() | |||
# Write an invalid source distribution | |||
pkman_path = sdist_dir / 'package.json5' | |||
pkman_path.write_text(json.dumps({})) | |||
tmp_project.build() | |||
pkman_path.write_text('lol') # Inavlid JSON | |||
tmp_project.build() | |||
pkman_path.write_text(''' | |||
{ | |||
name: 'invalid name', | |||
namespace: 'test', | |||
version: '1.2.3' | |||
} | |||
''') | |||
tmp_project.build() |