diff options
Diffstat (limited to 'bpkg/rep-fetch.cxx')
-rw-r--r-- | bpkg/rep-fetch.cxx | 708 |
1 files changed, 561 insertions, 147 deletions
diff --git a/bpkg/rep-fetch.cxx b/bpkg/rep-fetch.cxx index 3239421..d02a064 100644 --- a/bpkg/rep-fetch.cxx +++ b/bpkg/rep-fetch.cxx @@ -6,7 +6,7 @@ #include <map> #include <set> -#include <libbutl/manifest-parser.mxx> +#include <libbutl/manifest-parser.hxx> #include <bpkg/auth.hxx> #include <bpkg/fetch.hxx> @@ -15,7 +15,10 @@ #include <bpkg/package-odb.hxx> #include <bpkg/database.hxx> #include <bpkg/rep-remove.hxx> +#include <bpkg/pkg-verify.hxx> #include <bpkg/diagnostics.hxx> +#include <bpkg/satisfaction.hxx> +#include <bpkg/package-query.hxx> #include <bpkg/manifest-utility.hxx> using namespace std; @@ -49,9 +52,11 @@ namespace bpkg static rep_fetch_data rep_fetch_pkg (const common_options& co, const dir_path* conf, + database* db, const repository_location& rl, const optional<string>& dependent_trust, - bool ignore_unknown) + bool ignore_unknown, + bool ignore_toolchain) { // First fetch the repositories list and authenticate the base's // certificate. @@ -71,7 +76,7 @@ namespace bpkg if (a) { cert = authenticate_certificate ( - co, conf, cert_pem, rl, dependent_trust); + co, conf, db, cert_pem, rl, dependent_trust); a = !cert->dummy (); } @@ -113,6 +118,18 @@ namespace bpkg authenticate_repository (co, conf, cert_pem, *cert, sm, rl); } + // If requested, verify that the packages are compatible with the current + // toolchain. + // + if (!ignore_toolchain) + { + for (const package_manifest& m: fr.packages) + { + for (const dependency_alternatives& das: m.dependencies) + toolchain_buildtime_dependency (co, das, &m.name); + } + } + return rep_fetch_data {{move (fr)}, move (cert_pem), move (cert)}; } @@ -160,7 +177,8 @@ namespace bpkg M r; if (exists (f)) r = parse_manifest<M> (f, iu, rl, fragment); - else + + if (r.empty ()) r.emplace_back (repository_manifest ()); // Add the base repository. return r; @@ -189,81 +207,194 @@ namespace bpkg return r; } + static void + print_package_info (diag_record& dr, + const dir_path& pl, + const repository_location& rl, + const optional<string>& fragment) + { + dr << "package "; + + if (!pl.current ()) + dr << "'" << pl.string () << "' "; // Strip trailing '/'. + + dr << "in repository " << rl; + + if (fragment) + dr << ' ' << *fragment; + } + // Parse package manifests referenced by the package directory manifests. // - static vector<package_manifest> + static pair<vector<package_manifest>, vector<package_info>> parse_package_manifests (const common_options& co, const dir_path& repo_dir, - vector<package_manifest>&& sms, + vector<package_manifest>&& pms, bool iu, + bool it, const repository_location& rl, const optional<string>& fragment) // For diagnostics. { - vector<package_manifest> r; - r.reserve (sms.size ()); + auto prn_package_info = [&rl, &fragment] (diag_record& dr, + const package_manifest& pm) + { + print_package_info (dr, + path_cast<dir_path> (*pm.location), + rl, + fragment); + }; - for (package_manifest& sm: sms) + // Verify that all the package directories contain the package manifest + // files and retrieve the package versions via the single `b info` call, + // but only if the current build2 version is satisfactory for all the + // repository packages. While at it cache the manifest paths for the + // future use. + // + // Note that if the package is not compatible with the toolchain, not to + // end up with an unfriendly build2 error message (referring a line in the + // bootstrap file issued by the version module), we need to verify the + // compatibility of the package manifests prior to calling `b info`. Also + // note that we cannot create the package manifest objects at this stage, + // since we need the package versions for that. Thus, we cache the + // respective name value lists instead. + // + optional<package_version_infos> pvs; + paths mfs; + vector<vector<manifest_name_value>> nvs; { - assert (sm.location); + mfs.reserve (pms.size ()); + nvs.reserve (pms.size ()); - auto package_info = [&sm, &rl, &fragment] (diag_record& dr) + dir_paths pds; + pds.reserve (pms.size ()); + + // If true, then build2 version is satisfactory for all the repository + // packages. + // + bool bs (true); + + for (const package_manifest& pm: pms) { - dr << "package "; + assert (pm.location); - if (!sm.location->current ()) - dr << "'" << sm.location->string () << "' "; // Strip trailing '/'. + dir_path d (repo_dir / path_cast<dir_path> (*pm.location)); + d.normalize (); // In case location is './'. - dr << "in repository " << rl; + path f (d / manifest_file); + if (!exists (f)) + { + diag_record dr (fail); + dr << "no manifest file for "; + prn_package_info (dr, pm); + } - if (fragment) - dr << ' ' << *fragment; - }; + // Provide the context if the package compatibility verification fails. + // + auto g ( + make_exception_guard ( + [&pm, &prn_package_info] () + { + diag_record dr (info); - auto failure = [&package_info] (const char* desc) - { - diag_record dr (fail); - dr << desc << " for "; - package_info (dr); - }; + dr << "while retrieving information for "; + prn_package_info (dr, pm); + })); + + try + { + ifdstream ifs (f); + manifest_parser mp (ifs, f.string ()); - dir_path d (repo_dir / path_cast<dir_path> (*sm.location)); - d.normalize (); // In case location is './'. + // Note that the package directory points to something temporary + // (e.g., .bpkg/tmp/6f746365314d/) and it's probably better to omit + // it entirely (the above exception guard will print all we've got). + // + pkg_verify_result r (pkg_verify (co, mp, it, dir_path ())); + + if (bs && + r.build2_dependency && + !satisfy_build2 (co, *r.build2_dependency)) + { + bs = false; + pds.clear (); // Won't now be used. + } + + nvs.push_back (move (r)); + } + catch (const manifest_parsing& e) + { + fail (e.name, e.line, e.column) << e.description; + } + catch (const io_error& e) + { + fail << "unable to read from " << f << ": " << e; + } - path f (d / manifest_file); - if (!exists (f)) - failure ("no manifest file"); + mfs.push_back (move (f)); + + if (bs) + pds.push_back (move (d)); + } + + // Note that for the directory-based repositories we also query + // subprojects since the package information will be used for the + // subsequent package_iteration() call (see below). + // + if (bs) + pvs = package_versions (co, pds, + (rl.directory_based () + ? b_info_flags::subprojects + : b_info_flags::none)); + } + + // Parse package manifests, fixing up their versions. + // + pair<vector<package_manifest>, vector<package_info>> r; + r.first.reserve (pms.size ()); + + if (pvs) + r.second.reserve (pms.size ()); + + for (size_t i (0); i != pms.size (); ++i) + { + package_manifest& pm (pms[i]); + + assert (pm.location); try { - ifdstream ifs (f); - manifest_parser mp (ifs, f.string ()); - package_manifest m ( - mp, - [&co, &d] (version& v) + mfs[i].string (), + move (nvs[i]), + [&pvs, i] (version& v) { - if (optional<version> pv = package_version (co, d)) - v = move (*pv); + if (pvs) + { + optional<version>& pv ((*pvs)[i].version); + + if (pv) + v = move (*pv); + } }, iu); // Save the package manifest, preserving its location. // - m.location = move (*sm.location); - sm = move (m); + m.location = move (*pm.location); + + pm = move (m); } catch (const manifest_parsing& e) { diag_record dr (fail (e.name, e.line, e.column)); dr << e.description << info; - package_info (dr); - } - catch (const io_error& e) - { - fail << "unable to read from " << f << ": " << e; + prn_package_info (dr, pm); } - r.emplace_back (move (sm)); + r.first.push_back (move (pm)); + + if (pvs) + r.second.push_back (move ((*pvs)[i].info)); } return r; @@ -287,11 +418,11 @@ namespace bpkg ifdstream is (fp); string s (is.read_text ()); - if (s.empty ()) + if (s.empty () && name != "build-file") fail << name << " manifest value in " << pkg / manifest_file << " references empty file " << rp << info << "repository " << rl - << (!fragment.empty () ? " " + fragment : ""); + << (!fragment.empty () ? ' ' + fragment : ""); return s; } @@ -301,7 +432,7 @@ namespace bpkg << name << " manifest value in " << pkg / manifest_file << ": " << e << info << "repository " << rl - << (!fragment.empty () ? " " + fragment : "") << endf; + << (!fragment.empty () ? ' ' + fragment : "") << endf; } } @@ -309,7 +440,9 @@ namespace bpkg rep_fetch_dir (const common_options& co, const repository_location& rl, bool iu, - bool ev) + bool it, + bool ev, + bool lb) { assert (rl.absolute ()); @@ -330,29 +463,76 @@ namespace bpkg rl, string () /* fragment */)); - fr.packages = parse_package_manifests (co, - rd, - move (pms), - iu, - rl, - empty_string /* fragment */); + pair<vector<package_manifest>, vector<package_info>> pmi ( + parse_package_manifests (co, + rd, + move (pms), + iu, + it, + rl, + empty_string /* fragment */)); + + fr.packages = move (pmi.first); + fr.package_infos = move (pmi.second); - // Expand file-referencing package manifest values. + // If requested, expand file-referencing package manifest values and load + // the buildfiles into the respective *-build values. // - if (ev) + if (ev || lb) { for (package_manifest& m: fr.packages) - m.load_files ( - [&m, &rd, &rl] (const string& n, const path& p) - { - return read_package_file (p, - n, - path_cast<dir_path> (*m.location), - rd, - rl, - empty_string /* fragment */); - }, - iu); + { + dir_path pl (path_cast<dir_path> (*m.location)); + + // Load *-file values. + // + try + { + m.load_files ( + [ev, &rd, &rl, &pl] + (const string& n, const path& p) -> optional<string> + { + // Always expand the build-file values. + // + if (ev || n == "build-file") + { + return read_package_file (p, + n, + pl, + rd, + rl, + empty_string /* fragment */); + } + else + return nullopt; + }, + iu); + } + catch (const manifest_parsing& e) + { + diag_record dr (fail); + dr << e << info; + print_package_info (dr, pl, rl, nullopt /* fragment */); + dr << endf; + } + + // Load the bootstrap, root, and config/*.build buildfiles into the + // respective *-build values, if requested and if they are not already + // specified in the manifest. + // + if (lb) + try + { + load_package_buildfiles (m, rd / pl, true /* err_path_relative */); + } + catch (const runtime_error& e) + { + diag_record dr (fail); + dr << e << info; + print_package_info (dr, pl, rl, nullopt /* fragment */); + dr << endf; + } + } } return rep_fetch_data {{move (fr)}, @@ -365,16 +545,16 @@ namespace bpkg const dir_path* conf, const repository_location& rl, bool iu, - bool ev) + bool it, + bool ev, + bool lb) { - if (conf != nullptr && conf->empty ()) - conf = exists (bpkg_dir) ? ¤t_dir : nullptr; - - assert (conf == nullptr || !conf->empty ()); + auto i (tmp_dirs.find (conf != nullptr ? *conf : empty_dir_path)); + assert (i != tmp_dirs.end ()); dir_path sd (repository_state (rl)); - auto_rmdir rm (temp_dir / sd); + auto_rmdir rm (i->second / sd, !keep_tmp); const dir_path& td (rm.path); if (exists (td)) @@ -490,42 +670,91 @@ namespace bpkg // Parse package manifests. // - fr.packages = parse_package_manifests (co, - td, - move (pms), - iu, - rl, - fr.friendly_name); - - // Expand file-referencing package manifest values checking out - // submodules, if required. + pair<vector<package_manifest>, vector<package_info>> pmi ( + parse_package_manifests (co, + td, + move (pms), + iu, + it, + rl, + fr.friendly_name)); + + fr.packages = move (pmi.first); + fr.package_infos = move (pmi.second); + + // If requested, expand file-referencing package manifest values + // checking out submodules, if required, and load the buildfiles into + // the respective *-build values. // - if (ev) + if (ev || lb) { for (package_manifest& m: fr.packages) - m.load_files ( - [&m, &td, &rl, &fr, &checkout_submodules] (const string& n, - const path& p) - { - // Note that this doesn't work for symlinks on Windows where git - // normally creates filesystem-agnostic symlinks that are - // indistinguishable from regular files (see fixup_worktree() - // for details). It seems like the only way to deal with that is - // to unconditionally checkout submodules on Windows. Let's not - // pessimize things for now (if someone really wants this to - // work, they can always enable real symlinks in git). - // - if (!exists (td / *m.location / p)) - checkout_submodules (); - - return read_package_file (p, - n, - path_cast<dir_path> (*m.location), - td, - rl, - fr.friendly_name); - }, - iu); + { + dir_path pl (path_cast<dir_path> (*m.location)); + + // Load *-file values. + // + try + { + m.load_files ( + [ev, &td, &rl, &pl, &fr, &checkout_submodules] + (const string& n, const path& p) -> optional<string> + { + // Always expand the build-file values. + // + if (ev || n == "build-file") + { + // Check out submodules if the referenced file doesn't exist. + // + // Note that this doesn't work for symlinks on Windows where + // git normally creates filesystem-agnostic symlinks that + // are indistinguishable from regular files (see + // fixup_worktree() for details). It seems like the only way + // to deal with that is to unconditionally checkout + // submodules on Windows. Let's not pessimize things for now + // (if someone really wants this to work, they can always + // enable real symlinks in git). + // + if (!exists (td / pl / p)) + checkout_submodules (); + + return read_package_file (p, + n, + pl, + td, + rl, + fr.friendly_name); + } + else + return nullopt; + }, + iu); + } + catch (const manifest_parsing& e) + { + diag_record dr (fail); + dr << e << info; + print_package_info (dr, pl, rl, fr.friendly_name); + dr << endf; + } + + // Load the bootstrap, root, and config/*.build buildfiles into the + // respective *-build values, if requested and if they are not + // already specified in the manifest. + // + if (lb) + try + { + load_package_buildfiles (m, td / pl, true /* err_path_relative */); + } + catch (const runtime_error& e) + { + diag_record dr (fail); + dr << e << info; + print_package_info (dr, pl, rl, fr.friendly_name); + dr << endf; + } + } } np += fr.packages.size (); @@ -556,16 +785,28 @@ namespace bpkg static rep_fetch_data rep_fetch (const common_options& co, const dir_path* conf, + database* db, const repository_location& rl, const optional<string>& dt, bool iu, - bool ev) + bool it, + bool ev, + bool lb) { switch (rl.type ()) { - case repository_type::pkg: return rep_fetch_pkg (co, conf, rl, dt, iu); - case repository_type::dir: return rep_fetch_dir (co, rl, iu, ev); - case repository_type::git: return rep_fetch_git (co, conf, rl, iu, ev); + case repository_type::pkg: + { + return rep_fetch_pkg (co, conf, db, rl, dt, iu, it); + } + case repository_type::dir: + { + return rep_fetch_dir (co, rl, iu, it, ev, lb); + } + case repository_type::git: + { + return rep_fetch_git (co, conf, rl, iu, it, ev, lb); + } } assert (false); // Can't be here. @@ -577,9 +818,19 @@ namespace bpkg const dir_path* conf, const repository_location& rl, bool iu, - bool ev) + bool it, + bool ev, + bool lb) { - return rep_fetch (co, conf, rl, nullopt /* dependent_trust */, iu, ev); + return rep_fetch (co, + conf, + nullptr /* database */, + rl, + nullopt /* dependent_trust */, + iu, + it, + ev, + lb); } // Return an existing repository fragment or create a new one. Update the @@ -591,7 +842,7 @@ namespace bpkg static shared_ptr<repository_fragment> rep_fragment (const common_options& co, - const dir_path& conf, + database& db, transaction& t, const repository_location& rl, rep_fetch_data::fragment&& fr, @@ -601,7 +852,6 @@ namespace bpkg { tracer trace ("rep_fragment"); - database& db (t.database ()); tracer_guard tg (db, trace); // Calculate the fragment location. @@ -852,10 +1102,15 @@ namespace bpkg // details). // if (exists && !full_fetch) - rep_remove_package_locations (t, rf->name); + rep_remove_package_locations (db, t, rf->name); + + vector<package_manifest>& pms (fr.packages); + const vector<package_info>& pis (fr.package_infos); - for (package_manifest& pm: fr.packages) + for (size_t i (0); i != pms.size (); ++i) { + package_manifest& pm (pms[i]); + // Fix-up the external package version iteration number. // if (rl.directory_based ()) @@ -868,12 +1123,13 @@ namespace bpkg optional<version> v ( package_iteration ( co, - conf, + db, t, path_cast<dir_path> (rl.path () / *pm.location), pm.name, pm.version, - false /* check_external */)); + !pis.empty () ? &pis[i] : nullptr, + false /* check_external */)); if (v) pm.version = move (*v); @@ -956,7 +1212,7 @@ namespace bpkg // static void rep_fetch (const common_options& co, - const dir_path& conf, + database& db, transaction& t, const shared_ptr<repository>& r, const optional<string>& dependent_trust, @@ -970,7 +1226,6 @@ namespace bpkg { tracer trace ("rep_fetch(rep)"); - database& db (t.database ()); tracer_guard tg (db, trace); // Check that the repository is not fetched yet and register it as fetched @@ -990,7 +1245,8 @@ namespace bpkg // if (need_auth (co, r->location)) authenticate_certificate (co, - &conf, + &db.config_orig, + &db, r->certificate, r->location, dependent_trust); @@ -1057,13 +1313,22 @@ namespace bpkg // repository fragments list, as well as its prerequisite and complement // repository sets. // + // Note that we do this in the forward compatible manner ignoring + // unrecognized manifest values and unsatisfied build2 toolchain + // constraints in the package manifests. This approach allows older + // toolchains to work with newer repositories, successfully building the + // toolchain-satisfied packages and only failing for unsatisfied ones. + // rep_fetch_data rfd ( rep_fetch (co, - &conf, + &db.config_orig, + &db, rl, dependent_trust, true /* ignore_unknow */, - false /* expand_values */)); + true /* ignore_toolchain */, + false /* expand_values */, + true /* load_buildfiles */)); // Save for subsequent certificate authentication for repository use by // its dependents. @@ -1079,7 +1344,7 @@ namespace bpkg string nm (fr.friendly_name); // Don't move, still may be used. shared_ptr<repository_fragment> rf (rep_fragment (co, - conf, + db, t, rl, move (fr), @@ -1156,7 +1421,7 @@ namespace bpkg rm (pr); auto fetch = [&co, - &conf, + &db, &t, &fetched_repositories, &removed_repositories, @@ -1171,7 +1436,7 @@ namespace bpkg assert (i != repo_trust.end ()); rep_fetch (co, - conf, + db, t, r, i->second, @@ -1206,7 +1471,7 @@ namespace bpkg static void rep_fetch (const common_options& o, - const dir_path& conf, + database& db, transaction& t, const vector<lazy_shared_ptr<repository>>& repos, bool shallow, @@ -1215,7 +1480,6 @@ namespace bpkg { tracer trace ("rep_fetch(repos)"); - database& db (t.database ()); tracer_guard tg (db, trace); // As a fist step we fetch repositories recursively building the list of @@ -1243,7 +1507,7 @@ namespace bpkg // for (const lazy_shared_ptr<repository>& r: repos) rep_fetch (o, - conf, + db, t, r.load (), nullopt /* dependent_trust */, @@ -1258,7 +1522,14 @@ namespace bpkg // Remove dangling repositories. // for (const shared_ptr<repository>& r: removed_repositories) - rep_remove (conf, t, r); + { + // Prior to removing the repository we need to make sure it still + // exists, which may not be the case due to earlier removal of the + // dependent dangling repository. + // + if (db.find<repository> (r->name) != nullptr) + rep_remove (db, t, r); + } // Remove dangling repository fragments. // @@ -1277,7 +1548,7 @@ namespace bpkg // assert (f == rf); - rep_remove_fragment (conf, t, rf); + rep_remove_fragment (db, t, rf); } } @@ -1346,12 +1617,17 @@ namespace bpkg { dependencies& ds (at.package->dependencies); - // Note that the special test dependencies entry is always the last - // one, if present. + // Note that there is only one special test dependencies entry in + // the test package. // - assert (!ds.empty () && ds.back ().type); - - ds.pop_back (); + for (auto i (ds.begin ()), e (ds.end ()); i != e; ++i) + { + if (i->type) + { + ds.erase (i); + break; + } + } db.update (at.package); } @@ -1360,17 +1636,37 @@ namespace bpkg // Go through the available packages that have external tests and add // them as the special test dependencies to these test packages. // + // Note that not being able to resolve the test package for a main + // package is not an error, since the test package absence doesn't + // affect the main package building and internal testing. Dropping of an + // external test package from a repository may, however, be intentional. + // Think of a private repository crafted as a subset of some public + // repository with the external examples packages omitted. + // for (const auto& am: db.query<available_main> ()) { const shared_ptr<available_package>& p (am.package); + const package_name& n (p->id.name); + const version& v (p->version); vector<shared_ptr<repository_fragment>> rfs; for (const package_location& pl: p->locations) rfs.push_back (pl.repository_fragment.load ()); + bool module (build2_module (n)); + for (const test_dependency& td: p->tests) { + // Verify that the package has no runtime tests if it is a build + // system module. + // + if (module && !td.buildtime) + fail << "run-time " << td.type << ' ' << td.name << " for build " + << "system module " + << package_string (n, v) << + info << "build system modules cannot have run-time " << td.type; + vector<pair<shared_ptr<available_package>, shared_ptr<repository_fragment>>> tps ( filter (rfs, @@ -1386,11 +1682,122 @@ namespace bpkg dependencies& ds (tp->dependencies); - if (ds.empty () || !ds.back ().type) - ds.push_back (dependency_alternatives_ex (td.type)); + // Find the special test dependencies entry, if already present. + // + auto b (ds.begin ()); + auto e (ds.end ()); + auto oi (b); // Old entry location. + for (; oi != e && !oi->type; ++oi) ; + + // Note that since we store all the primary packages as + // alternative dependencies (which must be all of the same + // dependency type) for the test package, it must either be a + // runtime or build-time dependency for all of them. + // + // Note that the test package alternative dependencies contain the + // `== <version>` constraints (see below), so we can use min + // version of such a constraint as the primary package version. + // + if (oi != e && oi->buildtime != td.buildtime) + { + dependency_alternatives_ex& das (*oi); + assert (!das.empty ()); // Cannot be empty if present. + + const dependency_alternative& da (das[0]); + + // We always add the primary package to the test package as a + // single-dependency alternative (see below). + // + assert (da.size () == 1); + + fail << to_string (td.type) << " package " << td.name << " is a " + << "build-time dependency for one primary package and a " + << "run-time for another" << + info << (das.buildtime ? "build-time for " : "run-time for ") + << package_string (da[0].name, + *da[0].constraint->min_version) << + info << (td.buildtime ? "build-time for " : "run-time for ") + << package_string (n, v); + } + + // Find the (new) location for the special test dependencies entry. + // + // Note that if the entry is already present, it can only be moved + // towards the end of the list. + // + auto ni (e); + + // First, find the last depends clause that explicitly specifies + // this main package but goes after the special entry current + // location, if present. Note that we only consider clauses with + // the matching buildtime flag. + // + for (auto i (oi != e ? oi + 1 : b); i != e; ++i) + { + const dependency_alternatives_ex& das (*i); + if (das.buildtime == td.buildtime) + { + bool specifies (false); + + for (const dependency_alternative& da: das) + { + for (const dependency& d: da) + { + if (d.name == n) + { + specifies = true; + break; + } + } + + if (specifies) + break; + } + + if (specifies) + ni = i; + } + } + + // Now, set ni to refer to the special test dependencies entry, + // moving or creating one, if required. + // + if (oi != e) // The entry already exists? + { + if (ni != e) // Move the entry to the new location? + { + // Move the [oi + 1, ni] range 1 position to the left and + // move the *oi element to the now vacant ni slot. + // + rotate (oi, oi + 1, ni + 1); + } + else + ni = oi; // Leave the entry at the old location. + } + else // The entry doesn't exist. + { + if (ni != e) // Create the entry right after ni? + ++ni; + else + ni = b; // Create the entry at the beginning of the list. + + ni = ds.emplace (ni, td.type, td.buildtime); // Create the entry. + } + + // Finally, add the new dependency alternative to the special + // entry. + // + dependency_alternative da (td.enable, + td.reflect, + nullopt /* prefer */, + nullopt /* accept */, + nullopt /* require */); - ds.back ().push_back ( - dependency {p->id.name, version_constraint (p->version)}); + da.push_back (dependency {n, version_constraint (v)}); + + assert (ni != ds.end ()); // Must be deduced by now. + + ni->push_back (move (da)); db.update (tp); } @@ -1409,7 +1816,7 @@ namespace bpkg warn << "repository state is now broken and will be cleaned up" << info << "run 'bpkg rep-fetch' to update"; - rep_remove_clean (o, conf, t.database ()); + rep_remove_clean (o, db); } throw; @@ -1418,7 +1825,6 @@ namespace bpkg void rep_fetch (const common_options& o, - const dir_path& conf, database& db, const vector<repository_location>& rls, bool shallow, @@ -1444,13 +1850,17 @@ namespace bpkg // Add the repository, unless it is already a top-level one and has the // same location. // + // Note that on Windows we can overwrite the local repository location + // with the same location but some characters specified in a different + // case, which is ok. + // if (ua.find (r) == ua.end () || r.load ()->location.url () != rl.url ()) - rep_add (o, t, rl); + rep_add (o, db, t, rl); repos.emplace_back (r); } - rep_fetch (o, conf, t, repos, shallow, false /* full_fetch */, reason); + rep_fetch (o, db, t, repos, shallow, false /* full_fetch */, reason); t.commit (); } @@ -1467,7 +1877,11 @@ namespace bpkg // vector<lazy_shared_ptr<repository>> repos; - database db (open (c, trace)); + // Pre-attach the explicitly linked databases since we call + // package_iteration(). + // + database db (c, trace, true /* pre_attach */); + transaction t (db); session s; // Repository dependencies can have cycles. @@ -1531,7 +1945,7 @@ namespace bpkg // auto i (ua.find (r)); if (i == ua.end () || i->load ()->location.url () != rl.url ()) - r = lazy_shared_ptr<repository> (db, rep_add (o, t, rl)); + r = lazy_shared_ptr<repository> (db, rep_add (o, db, t, rl)); } repos.emplace_back (move (r)); @@ -1558,7 +1972,7 @@ namespace bpkg } } - rep_fetch (o, c, t, repos, o.shallow (), full_fetch, reason); + rep_fetch (o, db, t, repos, o.shallow (), full_fetch, reason); size_t rcount (0), pcount (0); if (verb) |