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.

185 lines
7.0KB

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