diff --git a/include/poac/core/resolver.hpp b/include/poac/core/resolver.hpp index a05e08755..13a0278ad 100644 --- a/include/poac/core/resolver.hpp +++ b/include/poac/core/resolver.hpp @@ -34,7 +34,9 @@ namespace poac::core::resolver { using termcolor2::color_literals::operator""_green; PLOG_INFO << fmt::format( - "{:>21} {} v{}", "Downloaded"_green, package.first, package.second + "{:>21} {} v{}", "Downloaded"_green, + resolve::get_name(package), + resolve::get_version(package) ); } return mitama::success(); @@ -57,8 +59,14 @@ namespace poac::core::resolver { try { const auto duplicate_deps = MITAMA_TRY(resolve::gather_all_deps(deps)); if (!resolve::duplicate_loose(duplicate_deps)) { - // When all dependencies are one package and one version, - // backtrack is not needed. + // When all dependencies are composed of one package and one version, + // a backtrack is not needed. Therefore, the duplicate_loose + // function just needs to check whether the gathered dependencies + // have multiple packages with the same name. If found multiple + // packages with the same name, then it means this package trying + // building depends on multiple versions of the same package. + // At the condition (the else clause), gathered dependencies + // should be in the backtrack loop. return mitama::success(resolve::activated_to_backtracked(duplicate_deps)); } else { return mitama::success(resolve::backtrack_loop(duplicate_deps)); diff --git a/include/poac/core/resolver/resolve.hpp b/include/poac/core/resolver/resolve.hpp index 11e7d6662..b906b2ef3 100644 --- a/include/poac/core/resolver/resolve.hpp +++ b/include/poac/core/resolver/resolve.hpp @@ -63,26 +63,36 @@ namespace poac::core::resolver::resolve { std::string // version or interval >; - package_t::first_type& + inline package_t::first_type& get_name(package_t& package) noexcept { return package.first; } - const package_t::first_type& + inline const package_t::first_type& get_name(const package_t& package) noexcept { return package.first; } - package_t::first_type& + inline package_t::second_type& get_version(package_t& package) noexcept { return package.second; } - const package_t::first_type& + inline const package_t::second_type& get_version(const package_t& package) noexcept { return package.second; } + inline package_t::second_type& + get_interval(package_t& package) noexcept { + return get_version(package); + } + + inline const package_t::second_type& + get_interval(const package_t& package) noexcept { + return get_version(package); + } + using deps_t = std::optional>; template <> @@ -107,6 +117,11 @@ namespace poac::core::resolver::resolve { std::string // version or interval >>; + inline const package_t& + get_package(const unique_deps_t::value_type& deps) noexcept { + return deps.first; + } + std::string to_binary_numbers(const int& x, const std::size_t& digit) { return fmt::format(FMT_STRING("{:0{}b}"), x, digit); } @@ -235,7 +250,7 @@ namespace poac::core::resolver::resolve { PLOG_DEBUG << fmt::format( "{}-{}: {}, ", - package.first, package.second, l + get_name(package), get_version(package), l ); } PLOG_DEBUG << ""; @@ -259,7 +274,7 @@ namespace poac::core::resolver::resolve { const auto last = std::end(rng); return std::find_if(first, last, [&](const auto& x){ return std::count_if(first, last, [&](const auto& y) { - return x.first == y.first; + return get_name(get_package(x)) == get_name(get_package(y)); }) > 1; }) != last; } @@ -269,11 +284,11 @@ namespace poac::core::resolver::resolve { // `latest` -> { 2.5.0 } // name is boost/config, no boost-config std::vector - get_versions_satisfy_interval(const std::string& name, const std::string& interval) { + get_versions_satisfy_interval(const package_t& package) { // TODO: (`>1.2 and <=1.3.2` -> NG,`>1.2.0-alpha and <=1.3.2` -> OK) const std::vector versions = - io::net::api::versions(name).unwrap(); - if (interval == "latest") { + io::net::api::versions(get_name(package)).unwrap(); + if (get_interval(package) == "latest") { return { *std::max_element( versions.cbegin(), @@ -285,7 +300,7 @@ namespace poac::core::resolver::resolve { }; } else { // `2.0.0` specific version or `>=0.1.2 and <3.4.0` version interval std::vector res; - semver::Interval i(name, interval); + semver::Interval i(get_interval(package)); std::copy_if( versions.cbegin(), versions.cend(), @@ -296,7 +311,7 @@ namespace poac::core::resolver::resolve { throw except::error( fmt::format( "`{}: {}` not found. seem dependencies are broken", - name, interval + get_name(package), get_interval(package) ) ); } else { @@ -308,38 +323,46 @@ namespace poac::core::resolver::resolve { using interval_cache_t = std::vector< std::tuple< - std::string, // name - std::string, // interval + package_t, std::vector // versions in the interval >>; + inline const package_t& + get_package(const interval_cache_t::value_type& cache) noexcept { + return std::get<0>(cache); + } + + inline const std::vector& + get_versions(const interval_cache_t::value_type& cache) noexcept { + return std::get<1>(cache); + } + duplicate_deps_t gather_deps_of_deps( const unique_deps_t& deps_api_res, interval_cache_t& interval_cache) { duplicate_deps_t cur_deps_deps; - for (const auto& [name, interval] : deps_api_res) { + for (const auto& package : deps_api_res) { // Check if node package is resolved dependency (by interval) const auto itr = boost::range::find_if( interval_cache, - [&name=name, &interval=interval](const auto& d) { - return std::get<0>(d) == name && - std::get<1>(d) == interval; + [&package](const auto& cache) { + return get_name(get_package(cache)) == get_name(package) && + get_interval(get_package(cache)) == get_interval(package); } ); if (itr != interval_cache.cend()) { // cache found - for (const auto& dep_version : std::get<2>(*itr)) { - cur_deps_deps.emplace_back(name, dep_version); + for (const auto& dep_version : get_versions(*itr)) { + cur_deps_deps.emplace_back(get_name(package), dep_version); } } else { - const auto dep_versions = - get_versions_satisfy_interval(name, interval); + const auto dep_versions = get_versions_satisfy_interval(package); // Cache interval and versions pair - interval_cache.emplace_back(name, interval, dep_versions); + interval_cache.emplace_back(package, dep_versions); for (const auto& dep_version : dep_versions) { - cur_deps_deps.emplace_back(name, dep_version); + cur_deps_deps.emplace_back(get_name(package), dep_version); } } } @@ -347,8 +370,7 @@ namespace poac::core::resolver::resolve { } void gather_deps( - const std::string& name, - const std::string& version, + const package_t& package, duplicate_deps_t& new_deps, interval_cache_t& interval_cache) { @@ -356,33 +378,34 @@ namespace poac::core::resolver::resolve { // (whether the specific version is the same), // and check circulating if (util::meta::find_if(new_deps, [&](const auto& d) { - return d.first.first == name && d.first.second == version; + return get_name(get_package(d)) == get_name(package) && + get_version(get_package(d)) == get_version(package); })) { return; } // Get dependencies of dependencies const unique_deps_t deps_api_res = - io::net::api::deps(name, version).unwrap(); + io::net::api::deps(get_name(package), get_version(package)).unwrap(); if (deps_api_res.empty()) { - new_deps.emplace_back(std::make_pair(name, version), std::nullopt); + new_deps.emplace_back(package, std::nullopt); } else { const auto deps_of_deps = gather_deps_of_deps(deps_api_res, interval_cache); // Store dependency and the dependency's dependencies. - new_deps.emplace_back(std::make_pair(name, version), deps_of_deps); + new_deps.emplace_back(package, deps_of_deps); // Gather dependencies of dependencies of dependencies. lol - for (const auto& [name, version] : deps_of_deps) { - gather_deps(name, version, new_deps, interval_cache); + for (const auto& dep_package : deps_of_deps) { + gather_deps(dep_package, new_deps, interval_cache); } } } [[nodiscard]] mitama::result, std::string> gather_all_deps(const unique_deps_t& deps) { - duplicate_deps_t duplicate_deps{}; - interval_cache_t interval_cache{}; + duplicate_deps_t duplicate_deps; + interval_cache_t interval_cache; // Activate the root of dependencies for (const auto& package : deps) { @@ -392,9 +415,9 @@ namespace poac::core::resolver::resolve { // by checking whether package's interval is the same. if (util::meta::find_if( interval_cache, - [&package=package](const auto& cache){ - return get_name(package) == std::get<0>(cache) && - get_version(package) == std::get<1>(cache); + [&package](const auto& cache){ + return get_name(package) == get_name(get_package(cache)) && + get_interval(package) == get_interval(get_package(cache)); } )) { continue; @@ -402,21 +425,12 @@ namespace poac::core::resolver::resolve { // Get versions using interval // FIXME: versions API and deps API are received the almost same responses - const auto versions = - get_versions_satisfy_interval( - get_name(package), - get_version(package) - ); + const auto versions = get_versions_satisfy_interval(package); // Cache interval and versions pair - interval_cache.emplace_back( - get_name(package), - get_version(package), - versions - ); + interval_cache.emplace_back(package, versions); for (const auto& version : versions) { gather_deps( - get_name(package), - version, + std::make_pair(get_name(package), version), duplicate_deps, interval_cache ); diff --git a/include/poac/util/semver/interval.hpp b/include/poac/util/semver/interval.hpp index 95d272146..7c6c08652 100644 --- a/include/poac/util/semver/interval.hpp +++ b/include/poac/util/semver/interval.hpp @@ -1,11 +1,14 @@ #ifndef SEMVER_INTERVAL_HPP #define SEMVER_INTERVAL_HPP +// std #include +#include #include #include #include +// internal #include #include @@ -18,7 +21,6 @@ namespace semver { class Interval { public: - const std::string name; const std::string interval; std::string comp_op; @@ -31,7 +33,7 @@ namespace semver { IntervalMode mode; - explicit Interval(const std::string& n, const std::string& i) : name(n), interval(i) { + explicit Interval(std::string_view i) : interval(i) { std::smatch match; if (std::regex_match(interval, match, std::regex(CLOSED_UNBOUNDED_INTERVAL))) { comp_op = match[2].str(); @@ -40,12 +42,10 @@ namespace semver { // Check if (comp_op.empty()) { // equal mode = IntervalMode::Equal; - } - else { + } else { mode = IntervalMode::ClosedUnbounded; } - } - else if (std::regex_match(interval, match, std::regex(BOUNDED_INTERVAL))) { + } else if (std::regex_match(interval, match, std::regex(BOUNDED_INTERVAL))) { first_comp_op = match[2].str(); second_comp_op = match[9].str(); @@ -65,10 +65,9 @@ namespace semver { throw std::range_error(error.value()); } mode = IntervalMode::Bounded; - } - else { + } else { throw std::invalid_argument( - "`" + name + ": " + interval + "` is invalid expression.\n" + "`" + interval + "` is invalid expression.\n" "Comparison operators:\n" " >, >=, <, <=\n" "Logical operator:\n" @@ -78,7 +77,7 @@ namespace semver { } } - bool satisfies(const std::string& version) const { + bool satisfies(std::string_view version) const { switch (mode) { case IntervalMode::Equal: return version == interval; @@ -95,17 +94,14 @@ namespace semver { private: // >2.3.0, 1.0.0, <=1.2.3-alpha, ... - bool satisfies_closed_unbounded_interval(const std::string& v) const { + bool satisfies_closed_unbounded_interval(std::string_view v) const { if (comp_op == ">") { return Version(v) > version_str; - } - else if (comp_op == ">=") { + } else if (comp_op == ">=") { return Version(v) >= version_str; - } - else if (comp_op == "<") { + } else if (comp_op == "<") { return Version(v) < version_str; - } - else if (comp_op == "<=") { + } else if (comp_op == "<=") { return Version(v) <= version_str; } return false; @@ -118,23 +114,20 @@ namespace semver { && (second_comp_op == "<" || second_comp_op == "<=")) { if (Version(first_version) > second_version) { // Prioritize the larger version - return "`" + name + ": " + interval + "` is invalid expression.\n" + return "`" + interval + "` is invalid expression.\n" "Did you mean " + first_comp_op + first_version + " ?"; - } - else { - return "`" + name + ": " + interval + "` is invalid expression.\n" + } else { + return "`" + interval + "` is invalid expression.\n" "Did you mean " + second_comp_op + second_version + " ?"; } - } - else if ((first_comp_op == ">" || first_comp_op == ">=") + } else if ((first_comp_op == ">" || first_comp_op == ">=") && (second_comp_op == ">" || second_comp_op == ">=")) { if (Version(first_version) < second_version) { // Prioritize the smaller version - return "`" + name + ": " + interval + "` is invalid expression.\n" + return "`" + interval + "` is invalid expression.\n" "Did you mean " + first_comp_op + first_version + " ?"; - } - else { - return "`" + name + ": " + interval + "` is invalid expression.\n" + } else { + return "`" + interval + "` is invalid expression.\n" "Did you mean " + second_comp_op + second_version + " ?"; } } @@ -154,19 +147,18 @@ namespace semver { if ((first_comp_op == "<" || first_comp_op == "<=") && (second_comp_op == ">" || second_comp_op == ">=")) { - return "`" + name + ": " + interval + "` is strange.\n" + return "`" + interval + "` is strange.\n" "In this case of interval specification using `and` +\n" " it is necessary to be a bounded interval.\n" "Please specify as in the following example:\n" "e.g. `" + second_comp_op + first_version + " and " + first_comp_op + second_version + "`"; } - } - else if (Version(first_version) > second_version) { + } else if (Version(first_version) > second_version) { if ((first_comp_op == ">" || first_comp_op == ">=") && (second_comp_op == "<" || second_comp_op == "<=")) { - return "`" + name + ": " + interval + "` is strange.\n" + return "`" + interval + "` is strange.\n" "In this case of interval specification using `and` +\n" " it is necessary to be a bounded interval.\n" "Please specify as in the following example:\n" @@ -177,36 +169,29 @@ namespace semver { return std::nullopt; } - bool satisfies_bounded_interval(const std::string& v) const { + bool satisfies_bounded_interval(std::string_view v) const { if (first_comp_op == ">") { if (second_comp_op == "<") { return (Version(v) > first_version) && (Version(v) < second_version); - } - else if (second_comp_op == "<=") { + } else if (second_comp_op == "<=") { return (Version(v) > first_version) && (Version(v) <= second_version); } - } - else if (first_comp_op == ">=") { + } else if (first_comp_op == ">=") { if (second_comp_op == "<") { return (Version(v) >= first_version) && (Version(v) < second_version); - } - else if (second_comp_op == "<=") { + } else if (second_comp_op == "<=") { return (Version(v) >= first_version) && (Version(v) <= second_version); } - } - else if (first_comp_op == "<") { + } else if (first_comp_op == "<") { if (second_comp_op == ">") { return (Version(v) < first_version) && (Version(v) > second_version); - } - else if (second_comp_op == ">=") { + } else if (second_comp_op == ">=") { return (Version(v) < first_version) && (Version(v) >= second_version); } - } - else if (first_comp_op == "<=") { + } else if (first_comp_op == "<=") { if (second_comp_op == ">") { return (Version(v) <= first_version) && (Version(v) > second_version); - } - else if (second_comp_op == ">=") { + } else if (second_comp_op == ">=") { return (Version(v) <= first_version) && (Version(v) >= second_version); } } diff --git a/include/poac/util/semver/version.hpp b/include/poac/util/semver/version.hpp index 20a751cc4..cf5c96ec3 100644 --- a/include/poac/util/semver/version.hpp +++ b/include/poac/util/semver/version.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -111,7 +112,8 @@ namespace semver { // The build metadata, ignored when determining version precedence. std::vector build = {}; - explicit Version(const std::string& version) { + explicit Version(std::string_view _version) { + const std::string version(_version); std::smatch match; if (std::regex_match(version, match, std::regex(FULL))) { apply_version(match); diff --git a/tests/semver/interval.cpp b/tests/semver/interval.cpp index cf8ed96e6..26dac1d87 100644 --- a/tests/semver/interval.cpp +++ b/tests/semver/interval.cpp @@ -6,7 +6,7 @@ BOOST_AUTO_TEST_CASE( semver_satisfies_test ) { using semver::Interval; - Interval interval("test", ">=1.66.0 and <1.70.0"); + Interval interval(">=1.66.0 and <1.70.0"); BOOST_CHECK( interval.satisfies("1.66.0") ); BOOST_CHECK( interval.satisfies("1.67.0") ); BOOST_CHECK( interval.satisfies("1.68.0") ); @@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE( semver_satisfies_test2 ) { using semver::Interval; - Interval interval("test", ">=1.0.0-alpha and <1.0.0"); + Interval interval(">=1.0.0-alpha and <1.0.0"); BOOST_CHECK( interval.satisfies("1.0.0-alpha") ); BOOST_CHECK( interval.satisfies("1.0.0-alpha.1") ); BOOST_CHECK( interval.satisfies("1.0.0-alpha.beta") ); @@ -34,29 +34,29 @@ BOOST_AUTO_TEST_CASE( semver_is_wasteful_comparison_operation_test ) { using semver::Interval; - BOOST_CHECK_THROW( Interval("test", "<2.0.0 and <1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", "<=2.0.0 and <=1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", "<2.0.0 and <=1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", "<=2.0.0 and <1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval("<2.0.0 and <1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval("<=2.0.0 and <=1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval("<2.0.0 and <=1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval("<=2.0.0 and <1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", "<1.0.0-alpha and <1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", "<1.0.0 and <1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval("<1.0.0-alpha and <1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval("<1.0.0 and <1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", ">2.0.0 and >1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", ">=2.0.0 and >=1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", ">2.0.0 and >=1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", ">=2.0.0 and >1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval(">2.0.0 and >1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval(">=2.0.0 and >=1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval(">2.0.0 and >=1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval(">=2.0.0 and >1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", ">1.0.0-alpha and >1.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", ">1.0.0 and >1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval(">1.0.0-alpha and >1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval(">1.0.0 and >1.0.0"), std::range_error ); } BOOST_AUTO_TEST_CASE( semver_is_bounded_interval_test ) { using semver::Interval; - BOOST_CHECK_THROW( Interval("test", "<1.0.0 and >2.0.0"), std::range_error ); - BOOST_CHECK_THROW( Interval("test", "<1.0.0-alpha and >1.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval("<1.0.0 and >2.0.0"), std::range_error ); + BOOST_CHECK_THROW( Interval("<1.0.0-alpha and >1.0.0"), std::range_error ); } // TODO: regex, Version constructor, get_version(), get_full(), Version exceptions