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/sync.cxx | 171 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 142 insertions(+), 29 deletions(-) (limited to 'bdep/sync.cxx') 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