From b3ca587b6c7c6f3f3c2bfa63629878c090f4a5a2 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 22 Feb 2018 00:32:30 +0300 Subject: Add support for version control-based repos to pkg-build --- bpkg/pkg-build.cxx | 232 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 138 insertions(+), 94 deletions(-) (limited to 'bpkg') diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index 67dbd51..dbb336d 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1613,19 +1614,23 @@ namespace bpkg // Ok, we have "all systems go". The overall action plan is as follows. // - // 1. disfigure up/down-graded, reconfigured [left to right] - // 2. purge up/down-graded - // 3. fetch new, up/down-graded - // 4. unpack new, up/down-graded - // 5. configure all [right to left] - // 6. build user selection [right to left] + // 1. disfigure up/down-graded, reconfigured [left to right] + // 2. purge up/down-graded [right to left] + // 3.a fetch/unpack new, up/down-graded + // 3.b checkout new, up/down-graded + // 4. configure all + // 5. build user selection [right to left] // // Note that for some actions, e.g., purge or fetch, the order is not // really important. We will, however, do it right to left since that // is the order closest to that of the user selection. // - // We are also going to combine purge/fetch/unpack into a single step - // and use the replace mode so it will become just fetch/unpack. + // We are also going to combine purge and fetch/unpack|checkout into a + // single step and use the replace mode so it will become just + // fetch/unpack|checkout. Configure will also be combined with the above + // operations to guarantee that prerequisite packages are configured by + // the time its dependents need to be checked out (see the pkg_checkout() + // function implementation for details). // // Almost forgot, there is one more thing: when we upgrade or downgrade a // package, it may change the list of its prerequisites. Which means we @@ -1704,120 +1709,159 @@ namespace bpkg } } - // purge/fetch/unpack + // purge, fetch/unpack|checkout, configure // for (build_package& p: reverse_iterate (pkgs)) { shared_ptr& sp (p.selected); const shared_ptr& ap (p.available); - if (ap == nullptr) // Skip dependents. - continue; - - // System package should not be fetched, it should only be configured on - // the next stage. Here we need to purge selected non-system package if - // present. Before we drop the object we need to make sure the hold - // state is preserved for the package being reconfigured. + // Purge the system package, fetch/unpack or checkout the source one. // - if (p.system) + for (;;) // Breakout loop. { - if (sp != nullptr && !sp->system ()) + if (ap == nullptr) // Skip dependents. + break; + + // System package should not be fetched, it should only be configured + // on the next stage. Here we need to purge selected non-system package + // if present. Before we drop the object we need to make sure the hold + // state is preserved for the package being reconfigured. + // + if (p.system) { - transaction t (db.begin ()); - pkg_purge (c, t, sp); // Commits the transaction. + if (sp != nullptr && !sp->system ()) + { + transaction t (db.begin ()); + pkg_purge (c, t, sp); // Commits the transaction. - if (verb) - text << "purged " << *sp; + if (verb) + text << "purged " << *sp; - if (!p.hold_package) - p.hold_package = sp->hold_package; + if (!p.hold_package) + p.hold_package = sp->hold_package; - if (!p.hold_version) - p.hold_version = sp->hold_version; + if (!p.hold_version) + p.hold_version = sp->hold_version; + + sp.reset (); + } - sp.reset (); + break; } - continue; - } + // Fetch or checkout if this is a new package or if we are + // up/down-grading. + // + if (sp == nullptr || sp->version != p.available_version ()) + { + sp.reset (); // For the directory case below. - // Fetch if this is a new package or if we are up/down-grading. - // - if (sp == nullptr || sp->version != p.available_version ()) - { - sp.reset (); // For the directory case below. + // Distinguish between the package and archive/directory cases. + // + const package_location& pl (ap->locations[0]); // Got to have one. - // Distinguish between the package and archive/directory cases. - // - const package_location& pl (ap->locations[0]); // Got to have one. + if (pl.repository.object_id () != "") // Special root? + { + // Go through package repositories to decide if we should fetch or + // checkout. Preferring a local one over the remotes seems like a + // sensible thing to do. + // + optional fetch; - if (pl.repository.object_id () != "") // Special root? - { - transaction t (db.begin ()); - sp = pkg_fetch (o, - c, - t, - ap->id.name, - p.available_version (), - true); // Replace; commits the transaction. - } - else if (exists (pl.location)) // Directory case is handled by unpack. - { - transaction t (db.begin ()); - sp = pkg_fetch (o, - c, - t, - pl.location, // Archive path. - true, // Replace - false); // Don't purge; commits the transaction. - } + for (const package_location& l: ap->locations) + { + const repository_location& rl (l.repository.load ()->location); - if (sp != nullptr) // Actually unpacked something? - { - assert (sp->state == package_state::fetched); + if (!fetch || rl.local ()) // First or local? + { + fetch = rl.archive_based (); - if (verb) - text << "fetched " << *sp; - } - } + if (rl.local ()) + break; + } + } - // Unpack. Note that the package can still be NULL if this is the - // directory case (see the fetch code above). - // - if (sp == nullptr || sp->state == package_state::fetched) - { - if (sp != nullptr) - { - transaction t (db.begin ()); - sp = pkg_unpack (o, c, t, ap->id.name); // Commits the transaction. - } - else - { - const package_location& pl (ap->locations[0]); - assert (pl.repository.object_id () == ""); // Special root. + assert (fetch); + + transaction t (db.begin ()); - transaction t (db.begin ()); - sp = pkg_unpack (c, + // Both calls commit the transaction. + // + sp = *fetch + ? pkg_fetch (o, + c, t, - path_cast (pl.location), - true, // Replace. - false); // Don't purge; commits the transaction. + ap->id.name, + p.available_version (), + true /* replace */) + : pkg_checkout (o, + c, + t, + ap->id.name, + p.available_version (), + true /* replace */); + } + // Directory case is handled by unpack. + // + else if (exists (pl.location)) + { + transaction t (db.begin ()); + sp = pkg_fetch ( + o, + c, + t, + pl.location, // Archive path. + true, // Replace + false); // Don't purge; commits the transaction. + } + + if (sp != nullptr) // Actually fetched or checked out something? + { + assert (sp->state == package_state::fetched || + sp->state == package_state::unpacked); // Checked out. + + if (verb) + text << (sp->state == package_state::fetched + ? "fetched " + : "checked out ") << *sp; + } } - assert (sp->state == package_state::unpacked); + // Unpack if required. Note that the package can still be NULL if this + // is the directory case (see the fetch code above). + // + if (sp == nullptr || sp->state == package_state::fetched) + { + if (sp != nullptr) + { + transaction t (db.begin ()); + sp = pkg_unpack (o, c, t, ap->id.name); // Commits the transaction. + } + else + { + const package_location& pl (ap->locations[0]); + assert (pl.repository.object_id () == ""); // Special root. + + transaction t (db.begin ()); + sp = pkg_unpack (c, + t, + path_cast (pl.location), + true, // Replace. + false); // Don't purge; commits the transaction. + } - if (verb) - text << "unpacked " << *sp; - } - } + assert (sp->state == package_state::unpacked); - // configure - // - for (build_package& p: reverse_iterate (pkgs)) - { - shared_ptr& sp (p.selected); - const shared_ptr& ap (p.available); + if (verb) + text << "unpacked " << *sp; + } + + break; // Get out from the breakout loop. + } + // Configure the package. + // // At this stage the package is either selected, in which case it's a // source code one, or just available, in which case it is a system // one. Note that a system package gets selected as being configured. -- cgit v1.1