You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
5.2KB

  1. #include "./cache.hpp"
  2. #include <dds/error/errors.hpp>
  3. #include <dds/pkg/db.hpp>
  4. #include <dds/sdist/dist.hpp>
  5. #include <dds/solve/solve.hpp>
  6. #include <dds/util/log.hpp>
  7. #include <dds/util/paths.hpp>
  8. #include <dds/util/string.hpp>
  9. #include <neo/ref.hpp>
  10. #include <range/v3/action/sort.hpp>
  11. #include <range/v3/action/unique.hpp>
  12. #include <range/v3/range/conversion.hpp>
  13. #include <range/v3/view/concat.hpp>
  14. #include <range/v3/view/filter.hpp>
  15. #include <range/v3/view/transform.hpp>
  16. using namespace dds;
  17. using namespace ranges;
  18. void pkg_cache::_log_blocking(path_ref dirpath) noexcept {
  19. dds_log(warn, "Another process has the package cache directory locked [{}]", dirpath.string());
  20. dds_log(warn, "Waiting for cache to be released...");
  21. }
  22. void pkg_cache::_init_cache_dir(path_ref dirpath) noexcept { fs::create_directories(dirpath); }
  23. fs::path pkg_cache::default_local_path() noexcept { return dds_data_dir() / "pkg"; }
  24. pkg_cache pkg_cache::_open_for_directory(bool writeable, path_ref dirpath) {
  25. auto try_read_sdist = [](path_ref p) -> std::optional<sdist> {
  26. if (starts_with(p.filename().string(), ".")) {
  27. return std::nullopt;
  28. }
  29. try {
  30. return sdist::from_directory(p);
  31. } catch (const std::runtime_error& e) {
  32. dds_log(error,
  33. "Failed to load source distribution from directory '{}': {}",
  34. p.string(),
  35. e.what());
  36. return std::nullopt;
  37. }
  38. };
  39. auto entries =
  40. // Get the top-level `name-version` dirs
  41. fs::directory_iterator(dirpath) //
  42. | neo::lref //
  43. // Convert each dir into an `sdist` object
  44. | ranges::views::transform(try_read_sdist) //
  45. // Drop items that failed to load
  46. | ranges::views::filter([](auto&& opt) { return opt.has_value(); }) //
  47. | ranges::views::transform([](auto&& opt) { return *opt; }) //
  48. | to<sdist_set>();
  49. return {writeable, dirpath, std::move(entries)};
  50. }
  51. void pkg_cache::import_sdist(const sdist& sd, if_exists ife_action) {
  52. neo_assertion_breadcrumbs("Importing sdist archive", sd.manifest.id.to_string());
  53. if (!_write_enabled) {
  54. dds_log(critical,
  55. "DDS attempted to write into a cache that wasn't opened with a write-lock. This "
  56. "is a hard bug and should be reported. For the safety and integrity of the local "
  57. "cache, we'll hard-exit immediately.");
  58. std::terminate();
  59. }
  60. auto sd_dest = _root / sd.manifest.id.to_string();
  61. if (fs::exists(sd_dest)) {
  62. auto msg = fmt::
  63. format("Package '{}' (Importing from [{}]) is already available in the local cache",
  64. sd.manifest.id.to_string(),
  65. sd.path.string());
  66. if (ife_action == if_exists::throw_exc) {
  67. throw_user_error<errc::sdist_exists>(msg);
  68. } else if (ife_action == if_exists::ignore) {
  69. dds_log(warn, msg);
  70. return;
  71. } else {
  72. dds_log(info, msg + " - Replacing");
  73. }
  74. }
  75. // Create a temporary location where we are creating it
  76. auto tmp_copy = sd_dest;
  77. tmp_copy.replace_filename(".tmp-import");
  78. if (fs::exists(tmp_copy)) {
  79. fs::remove_all(tmp_copy);
  80. }
  81. fs::create_directories(tmp_copy.parent_path());
  82. // Re-create an sdist from the given sdist. This will prune non-sdist files, rather than just
  83. // fs::copy_all from the source, which may contain extras.
  84. sdist_params params{
  85. .project_dir = sd.path,
  86. .dest_path = tmp_copy,
  87. .include_apps = true,
  88. .include_tests = true,
  89. };
  90. create_sdist_in_dir(tmp_copy, params);
  91. // Swap out the temporary to the final location
  92. if (fs::exists(sd_dest)) {
  93. fs::remove_all(sd_dest);
  94. }
  95. fs::rename(tmp_copy, sd_dest);
  96. _sdists.insert(sdist::from_directory(sd_dest));
  97. dds_log(info, "Source distribution for '{}' successfully imported", sd.manifest.id.to_string());
  98. }
  99. const sdist* pkg_cache::find(const pkg_id& pkg) const noexcept {
  100. auto found = _sdists.find(pkg);
  101. if (found == _sdists.end()) {
  102. return nullptr;
  103. }
  104. return &*found;
  105. }
  106. std::vector<pkg_id> pkg_cache::solve(const std::vector<dependency>& deps,
  107. const pkg_db& ctlg) const {
  108. return dds::solve(
  109. deps,
  110. [&](std::string_view name) -> std::vector<pkg_id> {
  111. auto mine = ranges::views::all(_sdists) //
  112. | ranges::views::filter(
  113. [&](const sdist& sd) { return sd.manifest.id.name.str == name; })
  114. | ranges::views::transform([](const sdist& sd) { return sd.manifest.id; });
  115. auto avail = ctlg.by_name(name);
  116. auto all = ranges::views::concat(mine, avail) | ranges::to_vector;
  117. ranges::sort(all, std::less{});
  118. ranges::unique(all, std::less{});
  119. return all;
  120. },
  121. [&](const pkg_id& pkg_id) {
  122. auto found = find(pkg_id);
  123. if (found) {
  124. return found->manifest.dependencies;
  125. }
  126. return ctlg.dependencies_of(pkg_id);
  127. });
  128. }