Selaa lähdekoodia

We have version ranges!

default_compile_flags
vector-of-bool 5 vuotta sitten
vanhempi
commit
a79be74102
12 muutettua tiedostoa jossa 249 lisäystä ja 102 poistoa
  1. +2
    -1
      library.dds
  2. +18
    -6
      src/dds/catalog/catalog.cpp
  3. +4
    -2
      src/dds/catalog/catalog.hpp
  4. +41
    -3
      src/dds/catalog/catalog.test.cpp
  5. +21
    -27
      src/dds/dds.main.cpp
  6. +2
    -33
      src/dds/deps.cpp
  7. +5
    -19
      src/dds/deps.hpp
  8. +31
    -2
      src/dds/repo/repo.cpp
  9. +3
    -1
      src/dds/repo/repo.hpp
  10. +4
    -8
      src/dds/sdist.hpp
  11. +102
    -0
      src/dds/solve/solve.cpp
  12. +16
    -0
      src/dds/solve/solve.hpp

+ 2
- 1
library.dds Näytä tiedosto

@@ -6,4 +6,5 @@ Uses: Niebler/range-v3
Uses: nlohmann/json
Uses: neo/buffer
Uses: neo/sqlite3
Uses: semver/semver
Uses: semver/semver
Uses: pubgrub/pubgrub

+ 18
- 6
src/dds/catalog/catalog.cpp Näytä tiedosto

@@ -1,5 +1,7 @@
#include "./catalog.hpp"

#include <dds/solve/solve.hpp>

#include <neo/sqlite3/exec.hpp>
#include <neo/sqlite3/iter_tuples.hpp>
#include <neo/sqlite3/single.hpp>
@@ -157,11 +159,16 @@ void catalog::store(const package_info& pkg) {
)"_sql);
for (const auto& dep : pkg.deps) {
new_dep_st.reset();
if (dep.versions.num_intervals() != 1) {
throw std::runtime_error(
"Package dependency may only contain a single version interval");
}
auto iv_1 = *dep.versions.iter_intervals().begin();
sqlite3::exec(new_dep_st,
std::forward_as_tuple(db_pkg_id,
dep.name,
dep.version_range.low().to_string(),
dep.version_range.high().to_string()));
iv_1.low.to_string(),
iv_1.high.to_string()));
}
}

@@ -244,9 +251,7 @@ std::vector<dependency> catalog::dependencies_of(const package_id& pkg) const no
std::forward_as_tuple(pkg.name, pkg.version.to_string())) //
| ranges::views::transform([](auto&& pair) {
auto& [name, low, high] = pair;
return dependency{name,
semver::range(semver::version::parse(low),
semver::version::parse(high))};
return dependency{name, {semver::version::parse(low), semver::version::parse(high)}};
}) //
| ranges::to_vector;
}
@@ -299,9 +304,10 @@ void catalog::import_json_str(std::string_view content) {
pkg_name,
version_,
dep_name));
auto range = semver::range::parse(std::string(dep_version));
info.deps.push_back({
std::string(dep_name),
semver::range::parse(std::string(dep_version)),
{range.low(), range.high()},
});
}

@@ -325,3 +331,9 @@ void catalog::import_json_str(std::string_view content) {
}
}
}

std::vector<package_id> catalog::solve_requirements(const std::vector<dependency>& deps) const {
return dds::solve(deps,
[&](std::string_view pkg_name) { return this->by_name(pkg_name); },
[&](const package_id& pkg) { return this->dependencies_of(pkg); });
}

+ 4
- 2
src/dds/catalog/catalog.hpp Näytä tiedosto

@@ -1,9 +1,9 @@
#pragma once

#include <dds/catalog/git.hpp>
#include <dds/deps.hpp>
#include <dds/package_id.hpp>
#include <dds/util/fs.hpp>
#include <dds/catalog/git.hpp>

#include <neo/sqlite3/database.hpp>
#include <neo/sqlite3/statement.hpp>
@@ -38,7 +38,7 @@ public:
static catalog open(const std::string& db_path);
static catalog open(path_ref db_path) { return open(db_path.string()); }

void store(const package_info& info);
void store(const package_info& info);
std::optional<package_info> get(const package_id& id) const noexcept;

std::vector<package_id> by_name(std::string_view sv) const noexcept;
@@ -49,6 +49,8 @@ public:
auto content = dds::slurp_file(json_path);
import_json_str(content);
}

std::vector<package_id> solve_requirements(const std::vector<dependency>& deps) const;
};

} // namespace dds

+ 41
- 3
src/dds/catalog/catalog.test.cpp Näytä tiedosto

@@ -34,8 +34,8 @@ TEST_CASE_METHOD(catalog_test_case, "Package requirements") {
db.store(dds::package_info{
dds::package_id{"foo", semver::version::parse("1.2.3")},
{
{"bar", semver::range::parse("=1.2.5")},
{"baz", semver::range::parse("^5.3.2")},
{"bar", {semver::version::parse("1.2.3"), semver::version::parse("1.4.0")}},
{"baz", {semver::version::parse("5.3.0"), semver::version::parse("6.0.0")}},
},
dds::git_remote_listing{"http://example.com", "master", std::nullopt},
});
@@ -72,5 +72,43 @@ TEST_CASE_METHOD(catalog_test_case, "Parse JSON repo") {
auto deps = db.dependencies_of(pkgs[0]);
REQUIRE(deps.size() == 1);
CHECK(deps[0].name == "bar");
CHECK(deps[0].version_range == semver::range::parse("~4.2.1"));
CHECK(deps[0].versions
== dds::version_range_set{semver::version::parse("4.2.1"),
semver::version::parse("4.3.0")});
}

TEST_CASE_METHOD(catalog_test_case, "Simple solve") {
db.import_json_str(R"({
"version": 1,
"packages": {
"foo": {
"1.2.3": {
"depends": {
"bar": "~4.2.1"
},
"git": {
"url": "http://example.com",
"ref": "master"
}
}
},
"bar": {
"4.2.3": {
"depends": {},
"git": {
"url": "http://example.com",
"ref": "master"
}
}
}
}
})");
auto sln = db.solve_requirements({{"foo",
dds::version_range_set{semver::version::parse("1.0.0"),
semver::version::parse("2.0.0")}}});
REQUIRE(sln.size() == 2);
CHECK(sln[0].name == "foo");
CHECK(sln[0].version == semver::version::parse("1.2.3"));
CHECK(sln[1].name == "bar");
CHECK(sln[1].version == semver::version::parse("4.2.3"));
}

+ 21
- 27
src/dds/dds.main.cpp Näytä tiedosto

@@ -541,7 +541,7 @@ struct cli_deps {
int run() {
const auto man = parent.load_package_manifest();
for (const auto& dep : man.dependencies) {
std::cout << dep.name << " " << dep.version_range.to_string() << '\n';
std::cout << dep.name << " " << dep.versions << '\n';
}
return 0;
}
@@ -557,34 +557,30 @@ struct cli_deps {
repo_path_flag repo_where{cmd};
catalog_path_flag catalog_path{cmd};


int run() {
auto man = parent.load_package_manifest();
auto catalog = catalog_path.open();
bool failed = false;
auto man = parent.load_package_manifest();
auto catalog = catalog_path.open();
bool failed = false;
auto solved_deps = catalog.solve_requirements(man.dependencies);
dds::repository::with_repository( //
repo_where.Get(),
dds::repo_flags::write_lock | dds::repo_flags::create_if_absent,
[&](dds::repository repo) {
for (auto& dep : man.dependencies) {
assert(false && "Not ready yet");
// auto exists = !!repo.find(dep.name, dep.version_range);
// if (!exists) {
// spdlog::info("Pull remote: {}@{}", dep.name,
// dep.version_range.to_string()); auto opt_pkg =
// catalog.get(dds::package_id{dep.name, dep.version_range}); if
// (opt_pkg) {
// auto tsd = dds::get_package_sdist(*opt_pkg);
// repo.add_sdist(tsd.sdist, dds::if_exists::ignore);
// } else {
// spdlog::error("No remote listing for {}@{}",
// dep.name,
// dep.version_range.to_string());
// failed = true;
// }
// } else {
// spdlog::info("Okay: {} {}", dep.name, dep.version_range.to_string());
// }
for (const dds::package_id& pk : solved_deps) {
auto exists = !!repo.find(pk);
if (!exists) {
spdlog::info("Pull remote: {}", pk.to_string());
auto opt_pkg = catalog.get(pk);
if (opt_pkg) {
auto tsd = dds::get_package_sdist(*opt_pkg);
repo.add_sdist(tsd.sdist, dds::if_exists::ignore);
} else {
spdlog::error("No remote listing for {}", pk.to_string());
failed = true;
}
} else {
spdlog::info("Okay: {}", pk.to_string());
}
}
});
if (failed) {
@@ -624,9 +620,7 @@ struct cli_deps {
repo_where.Get(),
dds::repo_flags::read,
[&](dds::repository repo) {
return find_dependencies(repo,
man.dependencies.begin(),
man.dependencies.end());
return repo.solve(man.dependencies);
});

auto tc = tc_filepath.get_toolchain();

+ 2
- 33
src/dds/deps.cpp Näytä tiedosto

@@ -10,6 +10,7 @@
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/transform.hpp>
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h>

#include <cctype>
#include <map>
@@ -31,7 +32,7 @@ dependency dependency::parse_depends_string(std::string_view str) {

try {
auto rng = semver::range::parse_restricted(version_str);
return dependency{std::string(name), rng};
return dependency{std::string(name), {rng.low(), rng.high()}};
} catch (const semver::invalid_range&) {
throw std::runtime_error(fmt::format(
"Invalid version range string '{}' in dependency declaration '{}' (Should be a "
@@ -41,38 +42,6 @@ dependency dependency::parse_depends_string(std::string_view str) {
}
}

std::vector<sdist> dds::find_dependencies(const repository& repo, const dependency& dep) {
std::vector<sdist> acc;
detail::do_find_deps(repo, dep, acc);
return acc;
}

void detail::do_find_deps(const repository& repo, const dependency& dep, std::vector<sdist>& sd) {
auto sdist_opt = repo.find(dep.name, dep.version_range.low());
if (!sdist_opt) {
throw std::runtime_error(
fmt::format("Unable to find dependency to satisfy requirement: {} {}",
dep.name,
dep.version_range.to_string()));
}
const sdist& new_sd = *sdist_opt;
for (const auto& inner_dep : new_sd.manifest.dependencies) {
do_find_deps(repo, inner_dep, sd);
}
auto insert_point = std::partition_point(sd.begin(), sd.end(), [&](const sdist& cand) {
return cand.path < new_sd.path;
});
if (insert_point != sd.end()
&& insert_point->manifest.pk_id.name == new_sd.manifest.pk_id.name) {
if (insert_point->manifest.pk_id.version != new_sd.manifest.pk_id.version) {
assert(false && "Version conflict resolution not implemented yet");
std::terminate();
}
return;
}
sd.insert(insert_point, std::move(new_sd));
}

using sdist_index_type = std::map<std::string, std::reference_wrapper<const sdist>>;
using sdist_names = std::set<std::string>;


+ 5
- 19
src/dds/deps.hpp Näytä tiedosto

@@ -2,6 +2,7 @@

#include <dds/build/plan/full.hpp>

#include <pubgrub/interval.hpp>
#include <semver/range.hpp>
#include <semver/version.hpp>

@@ -19,30 +20,15 @@ enum class version_strength {
major,
};

using version_range_set = pubgrub::interval_set<semver::version>;

struct dependency {
std::string name;
semver::range version_range;
std::string name;
version_range_set versions;

static dependency parse_depends_string(std::string_view str);
};

namespace detail {

void do_find_deps(const repository&, const dependency& dep, std::vector<sdist>& acc);

} // namespace detail

std::vector<sdist> find_dependencies(const repository& repo, const dependency& dep);

template <typename Iter, typename Snt>
inline std::vector<sdist> find_dependencies(const repository& repo, Iter it, Snt stop) {
std::vector<sdist> acc;
while (it != stop) {
detail::do_find_deps(repo, *it++, acc);
}
return acc;
}

build_plan create_deps_build_plan(const std::vector<sdist>& deps, build_env_ref env);

void write_libman_index(path_ref where, const build_plan& plan, const build_env& env);

+ 31
- 2
src/dds/repo/repo.cpp Näytä tiedosto

@@ -1,11 +1,13 @@
#include "./repo.hpp"

#include <dds/sdist.hpp>
#include <dds/solve/solve.hpp>
#include <dds/util/paths.hpp>
#include <dds/util/string.hpp>

#include <spdlog/spdlog.h>

#include <range/v3/action/sort.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/transform.hpp>
@@ -96,10 +98,37 @@ void repository::add_sdist(const sdist& sd, if_exists ife_action) {
spdlog::info("Source distribution '{}' successfully exported", sd.manifest.pk_id.to_string());
}

const sdist* repository::find(std::string_view name, semver::version ver) const noexcept {
auto found = _sdists.find(std::tie(name, ver));
const sdist* repository::find(const package_id& pkg) const noexcept {
auto found = _sdists.find(pkg);
if (found == _sdists.end()) {
return nullptr;
}
return &*found;
}

std::vector<sdist> repository::solve(const std::vector<dependency>& deps) const {
auto ids = dds::solve(deps,
[&](std::string_view name) -> std::vector<package_id> {
auto items = ranges::views::all(_sdists) //
| ranges::views::filter([&](const sdist& sd) {
return sd.manifest.pk_id.name == name;
})
| ranges::views::transform(
[](const sdist& sd) { return sd.manifest.pk_id; })
| ranges::to_vector;
ranges::sort(items, std::less<>{});
return items;
},
[&](const package_id& pkg_id) {
auto found = find(pkg_id);
assert(found);
return found->manifest.dependencies;
});
return ids //
| ranges::views::transform([&](const package_id& pk_id) {
auto found = find(pk_id);
assert(found);
return *found;
}) //
| ranges::to_vector;
}

+ 3
- 1
src/dds/repo/repo.hpp Näytä tiedosto

@@ -80,7 +80,7 @@ public:

void add_sdist(const sdist&, if_exists = if_exists::throw_exc);

const sdist* find(std::string_view name, semver::version ver) const noexcept;
const sdist* find(const package_id& pk) const noexcept;

auto iter_sdists() const noexcept {
class ret {
@@ -95,6 +95,8 @@ public:
} r{_sdists};
return r;
}

std::vector<sdist> solve(const std::vector<dependency>& deps) const;
};

} // namespace dds

+ 4
- 8
src/dds/sdist.hpp Näytä tiedosto

@@ -30,15 +30,11 @@ inline constexpr struct sdist_compare_t {
bool operator()(const sdist& lhs, const sdist& rhs) const {
return lhs.manifest.pk_id < rhs.manifest.pk_id;
}
template <typename Name, typename Version>
bool operator()(const sdist& lhs, const std::tuple<Name, Version>& rhs) const {
auto&& [name, ver] = rhs;
return lhs.manifest.pk_id < package_id{name, ver};
bool operator()(const sdist& lhs, const package_id& rhs) const {
return lhs.manifest.pk_id < rhs;
}
template <typename Name, typename Version>
bool operator()(const std::tuple<Name, Version>& lhs, const sdist& rhs) const {
auto&& [name, ver] = lhs;
return package_id{name, ver} < rhs.manifest.pk_id;
bool operator()(const package_id& lhs, const sdist& rhs) const {
return lhs < rhs.manifest.pk_id;
}
using is_transparent = int;
} sdist_compare;

+ 102
- 0
src/dds/solve/solve.cpp Näytä tiedosto

@@ -0,0 +1,102 @@
#include "./solve.hpp"

#include <pubgrub/solve.hpp>

#include <range/v3/range/conversion.hpp>
#include <range/v3/view/transform.hpp>

using namespace dds;

namespace {

struct req_type {
dependency dep;

using req_ref = const req_type&;

bool implied_by(req_ref other) const noexcept {
return dep.versions.contains(other.dep.versions);
}

bool excludes(req_ref other) const noexcept {
return dep.versions.disjoint(other.dep.versions);
}

std::optional<req_type> intersection(req_ref other) const noexcept {
auto range = dep.versions.intersection(other.dep.versions);
if (range.empty()) {
return std::nullopt;
}
return req_type{dependency{dep.name, std::move(range)}};
}

std::optional<req_type> union_(req_ref other) const noexcept {
auto range = dep.versions.union_(other.dep.versions);
if (range.empty()) {
return std::nullopt;
}
return req_type{dependency{dep.name, std::move(range)}};
}

std::optional<req_type> difference(req_ref other) const noexcept {
auto range = dep.versions.difference(other.dep.versions);
if (range.empty()) {
return std::nullopt;
}
return req_type{dependency{dep.name, std::move(range)}};
}

auto key() const noexcept { return dep.name; }

friend bool operator==(req_ref lhs, req_ref rhs) noexcept {
return lhs.dep.name == rhs.dep.name && lhs.dep.versions == rhs.dep.versions;
}
};

auto as_pkg_id(const req_type& req) {
const version_range_set& versions = req.dep.versions;
assert(versions.num_intervals() == 1);
return package_id{req.dep.name, (*versions.iter_intervals().begin()).low};
}

struct solver_provider {
pkg_id_provider_fn& pkgs_for_name;
deps_provider_fn& deps_for_pkg;

mutable std::map<std::string, std::vector<package_id>> pkgs_by_name = {};

std::optional<req_type> best_candidate(const req_type& req) const {
auto found = pkgs_by_name.find(req.dep.name);
if (found == pkgs_by_name.end()) {
found = pkgs_by_name.emplace(req.dep.name, pkgs_for_name(req.dep.name)).first;
}
auto& vec = found->second;
auto cand = std::find_if(vec.cbegin(), vec.cend(), [&](const package_id& pk) {
return req.dep.versions.contains(pk.version);
});
if (cand == vec.cend()) {
return std::nullopt;
}
return req_type{dependency{cand->name, {cand->version, cand->version.next_after()}}};
}

std::vector<req_type> requirements_of(const req_type& req) const {
auto pk_id = as_pkg_id(req);
auto deps = deps_for_pkg(pk_id);
return deps //
| ranges::views::transform([](const dependency& dep) { return req_type{dep}; }) //
| ranges::to_vector;
}
};

} // namespace

std::vector<package_id> dds::solve(const std::vector<dependency>& deps,
pkg_id_provider_fn pkgs_prov,
deps_provider_fn deps_prov) {
auto wrap_req
= deps | ranges::v3::views::transform([](const dependency& dep) { return req_type{dep}; });

auto solution = pubgrub::solve(wrap_req, solver_provider{pkgs_prov, deps_prov});
return solution | ranges::views::transform(as_pkg_id) | ranges::to_vector;
}

+ 16
- 0
src/dds/solve/solve.hpp Näytä tiedosto

@@ -0,0 +1,16 @@
#pragma once

#include <dds/deps.hpp>
#include <dds/package_id.hpp>

#include <functional>

namespace dds {

using pkg_id_provider_fn = std::function<std::vector<package_id>(std::string_view)>;
using deps_provider_fn = std::function<std::vector<dependency>(const package_id& pk)>;

std::vector<package_id>
solve(const std::vector<dependency>& deps, pkg_id_provider_fn, deps_provider_fn);

} // namespace dds

Loading…
Peruuta
Tallenna