From 9defb02d42361b28d5ec345b5f65a8f2790fb2aa Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 28 Mar 2018 14:06:10 +0200 Subject: Handle sync's upgrade forms --- bdep/status.cxx | 26 ++++----- bdep/sync.cxx | 171 ++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 154 insertions(+), 43 deletions(-) diff --git a/bdep/status.cxx b/bdep/status.cxx index 814e21b..beb8e4f 100644 --- a/bdep/status.cxx +++ b/bdep/status.cxx @@ -19,7 +19,7 @@ namespace bdep cmd_status (const cmd_status_options& o, const dir_path& prj, const dir_path& cfg, - const cstrings& pkgs, + const strings& pkgs, bool fetch) { // Shallow fetch the project to make sure we show latest iterations and @@ -61,17 +61,17 @@ namespace bdep // If no packages were explicitly specified, then we print the status for // all that have been initialized in the configuration. // - cstrings pkgs; + strings pkgs; if (ps.empty ()) { for (const package_state& p: c->packages) - pkgs.push_back (p.name.c_str ()); + pkgs.push_back (p.name); } else { for (const package_location& p: ps) - pkgs.push_back (p.name.c_str ()); + pkgs.push_back (p.name); } cmd_status (o, prj, c->path, pkgs, fetch); @@ -85,18 +85,18 @@ namespace bdep if (o.immediate () && o.recursive ()) fail << "both --immediate|-i and --recursive|-r specified"; - // We have two pretty different modes: project status and dependency - // status (have arguments). + // We have two pretty different modes: project package status and + // dependency package status (have arguments). // - cstrings pkgs; - for (; args.more (); pkgs.push_back (args.next ())) ; + strings dep_pkgs; + for (; args.more (); dep_pkgs.push_back (args.next ())) ; // For the project status the same story as in sync. // project_packages pp ( find_project_packages (o, - !pkgs.empty () /* ignore_packages */, - false /* load_packages */)); + !dep_pkgs.empty () /* ignore_packages */, + false /* load_packages */)); const dir_path& prj (pp.project); @@ -140,12 +140,10 @@ namespace bdep if (fetch) cmd_fetch (o, prj, c, o.fetch_full ()); - // Don't re-fetch if we just fetched. - // - if (pkgs.empty ()) + if (dep_pkgs.empty ()) cmd_status (o, prj, c, pp.packages, !fetch); else - cmd_status (o, prj, c->path, pkgs, !fetch); + cmd_status (o, prj, c->path, dep_pkgs, !fetch); } return 0; diff --git a/bdep/sync.cxx b/bdep/sync.cxx index 3d2ee9c..526ac4a 100644 --- a/bdep/sync.cxx +++ b/bdep/sync.cxx @@ -13,14 +13,24 @@ using namespace std; namespace bdep { - void + // Sync with optional upgrade. + // + // If upgrade is not nullopt, then: If there are dep_pkgs, then we are + // upgrading specific dependency packages. Othewise -- project packages. + // + static void cmd_sync (const common_options& co, const dir_path& prj, const shared_ptr& c, bool fetch, - bool yes) + bool yes, + optional upgrade, // true - upgrade, false - patch + optional recursive, // true - recursive, false - immediate + const package_locations& prj_pkgs, + const strings& dep_pkgs) { assert (!c->packages.empty ()); + assert (prj_pkgs.empty () || dep_pkgs.empty ()); // Can't have both. // Do a separate fetch instead of letting pkg-build do it. This way we get // better control of the diagnostics (no "fetching ..." for the project @@ -31,7 +41,7 @@ namespace bdep // "I want this dependency upgraded to the latest version available from // this repository"). // - // We also use the repository name rather than then location as a sanity + // We also use the repository name rather than the location as a sanity // check (the repository must have been added as part of init). // if (fetch) @@ -41,43 +51,106 @@ namespace bdep "--shallow", "dir:" + prj.string ()); - // Prepare the pkg-spec. + // Prepare the package list. // - string spec; + strings args; + for (const package_state& p: c->packages) { - if (!spec.empty ()) - spec += ','; + if (upgrade) + { + // We synchronize all the init'ed packages but only upgrade the + // specified. + // + if (find_if (prj_pkgs.begin (), + prj_pkgs.end (), + [&p] (const package_location& pl) + { + return pl.name == p.name; + }) != prj_pkgs.end ()) + { + // The project package itself must always be upgraded to the latest + // version/iteration. So we have to translate our option to their + // dependency-only --{upgrade,patch}-{recursive,immediate}. + // + args.push_back ("{"); + args.push_back ( + *upgrade + ? *recursive ? "--upgrade-recursive" : "--upgrade-immediate" + : *recursive ? "--patch-recursive" : "--patch-immediate"); + args.push_back ("}+"); + } + } - spec += p.name; + // We need to add the explicit location qualification (@) since + // there is no guarantee a higher version isn't available from another + // repository. + // + args.push_back (p.name + '@' + prj.string ()); } - spec += '@'; - spec += prj.string (); + if (upgrade) + { + for (const string& p: dep_pkgs) + { + // Unless this is the default "non-recursive upgrade" we need to add a + // group. + // + if (recursive || !*upgrade) + { + args.push_back ("{"); + args.push_back (*upgrade ? "-u" : "-p"); + if (recursive) args.push_back (*recursive ? "-r" : "-i"); + args.push_back ("}+"); + } + + // Make sure it is treated as a dependency. + // + args.push_back ('?' + p); + } + } - // It feels right to drop unused dependencies without any confirmation. - // // @@ TODO: right now it is silent. Can we print a plan without the // prompts? Probably also a good idea even if from build system // hook... Issue: drop dependents has no "drop " prefix. Also indented - // stuff out of nowhere will look odd. + // stuff out of nowhere will look odd. -- Need custom prompt. // run_bpkg (co, "build", "-d", c->path, "--no-fetch", "--configure-only", - "--drop-prerequisite", "--keep-out", (yes ? "--yes" : nullptr), - spec); + args); + } + + void + cmd_sync (const common_options& co, + const dir_path& prj, + const shared_ptr& c, + bool fetch, + bool yes) + { + cmd_sync (co, + prj, + c, + fetch, + yes, + nullopt /* upgrade */, + nullopt /* recursive */, + package_locations () /* prj_pkgs */, + strings () /* dep_pkgs */); } int - cmd_sync (const cmd_sync_options& o, cli::scanner&) + cmd_sync (const cmd_sync_options& o, cli::scanner& args) { tracer trace ("sync"); + if (o.upgrade () && o.patch ()) + fail << "both --upgrade|-u and --patch|-p specified"; + // The --immediate or --recursive option can only be specified with // an explicit --upgrade or --patch. // @@ -88,19 +161,26 @@ namespace bdep fail << n << " requires explicit --upgrade|-u or --patch|-p"; } + // We have two pretty different upgrade modes: project package upgrade and + // dependency package upgrade (have arguments). + // + strings dep_pkgs; + for (; args.more (); dep_pkgs.push_back (args.next ())) ; + // We could be running from a package directory (or the user specified one - // with -d) that has not been init'ed in this configuration. We want to - // diagnose that since such a package will not be present in the bpkg - // configuration. But if we are running from the project, then we don't - // want to treat all the available packages as specified by the user (thus - // load_packages=false). + // with -d) that has not been init'ed in this configuration. Unless we are + // upgrading specific dependencies, we want to diagnose that since such a + // package will not be present in the bpkg configuration. But if we are + // running from the project, then we don't want to treat all the available + // packages as specified by the user (thus load_packages=false). // project_packages pp ( find_project_packages (o, - false /* ignore_packages */, - false /* load_packages */)); + !dep_pkgs.empty () /* ignore_packages */, + false /* load_packages */)); const dir_path& prj (pp.project); + const package_locations& prj_pkgs (pp.packages); database db (open (prj, trace)); @@ -110,7 +190,7 @@ namespace bdep // If specified, verify packages are present in each configuration. // - if (!pp.packages.empty ()) + if (!prj_pkgs.empty ()) verify_project_packages (pp, cfgs); // Synchronize each configuration skipping empty ones. @@ -142,11 +222,44 @@ namespace bdep if (fetch) cmd_fetch (o, prj, c, o.fetch_full ()); - // Don't re-fetch if we just fetched. - // - cmd_sync (o, prj, c, !fetch); - - //@@ TODO: sync upgrade (see status for structure ideas). Pass o.yes(). + if (!dep_pkgs.empty ()) + { + // The third form: upgrade of the specified dependencies. + // + // Only prompt if upgrading their dependencies. + // + cmd_sync (o, + prj, + c, + !fetch, + o.recursive () || o.immediate () ? o.yes () : true, + !o.patch (), // Upgrade by default unless patch requested. + (o.recursive () ? optional (true) : + o.immediate () ? optional (false) : nullopt), + prj_pkgs, + dep_pkgs); + } + else if (o.upgrade () || o.patch ()) + { + // The second form: upgrade of project packages' dependencies + // (immediate by default, recursive if requested). + // + cmd_sync (o, + prj, + c, + !fetch, + o.yes (), + o.upgrade (), + o.recursive (), + prj_pkgs, + dep_pkgs); + } + else + { + // The first form: sync of project packages. + // + cmd_sync (o, prj, c, !fetch, true /* yes */); + } } return 0; -- cgit v1.1