From 07ab59a93f9447d5489743e8d7e19b6adb5ebbf1 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 9 Jul 2021 20:55:15 +0300 Subject: Add support for configuration type and bdep-config-link sub-command --- bdep/build.txx | 78 +++++++++------- bdep/ci.cli | 14 +-- bdep/ci.cxx | 92 +++++++++++++------ bdep/clean.cli | 6 +- bdep/config.cli | 49 ++++++---- bdep/config.cxx | 230 ++++++++++++++++++++++++++++++++++++---------- bdep/config.hxx | 7 +- bdep/database.cxx | 18 ++++ bdep/deinit.cli | 4 +- bdep/deinit.cxx | 93 +++++++++++-------- bdep/fetch.cli | 4 +- bdep/fetch.cxx | 2 +- bdep/init.cli | 9 +- bdep/init.cxx | 169 +++++++++++++++++++++++++--------- bdep/init.hxx | 2 +- bdep/new.cxx | 12 ++- bdep/project.cli | 8 ++ bdep/project.cxx | 43 ++++++--- bdep/project.hxx | 24 ++++- bdep/project.xml | 6 ++ bdep/projects-configs.cli | 11 ++- bdep/publish.cli | 4 +- bdep/publish.cxx | 99 +++++++++++++------- bdep/status.cli | 4 +- bdep/status.cxx | 102 +++++++++++--------- bdep/sync.cli | 4 +- bdep/sync.cxx | 35 ++++--- bdep/test.cli | 6 +- bdep/update.cli | 6 +- tests/ci.testscript | 26 +++++- tests/config.testscript | 85 +++++++++++++---- tests/init.testscript | 18 ++-- tests/new.testscript | 4 +- tests/publish.testscript | 44 ++++++++- tests/update.testscript | 43 +++++++++ 35 files changed, 980 insertions(+), 381 deletions(-) diff --git a/bdep/build.txx b/bdep/build.txx index 0090be5..7d17088 100644 --- a/bdep/build.txx +++ b/bdep/build.txx @@ -44,56 +44,72 @@ namespace bdep const dir_path& prj (pp.project); - // Load the configurations without keeping the database open longer - // than necessary. + // Load the configurations without keeping the database open longer than + // necessary. // configurations cfgs; { database db (open (prj, trace)); transaction t (db.begin ()); - cfgs = find_configurations (o, prj, t); + pair cs (find_configurations (o, prj, t)); t.commit (); - } - // If specified, verify packages are present in each configuration. - // - if (!pp.packages.empty ()) - verify_project_packages (pp, cfgs); + // If specified, verify packages are present in at least one + // configuration. + // + if (!pp.packages.empty ()) + verify_project_packages (pp, cs); + + cfgs = move (cs.first); + } // If no packages were explicitly specified, then we build all that have - // been initialized in each configuration. + // been initialized in each configuration. Otherwise, we build only + // specified packages initialized in the (specified) configurations. // - cstrings pkgs; + const package_locations& pkgs (pp.packages); - bool all (pp.packages.empty ()); - if (!all) - { - for (const package_location& p: pp.packages) - pkgs.push_back (p.name.string ().c_str ()); - } + bool all (pkgs.empty ()); - // Build in each configuration skipping empty ones. + // Build in each configuration, skipping those where no packages needs to + // be built. // bool first (true); for (const shared_ptr& c: cfgs) { - if (c->packages.empty ()) - { - if (verb) - info << "skipping empty configuration " << *c; + // Collect packages to build. + // + cstrings ps; - continue; + for (const package_state& s: c->packages) + { + if (all || + find_if (pkgs.begin (), + pkgs.end (), + [&s] (const package_location& p) + { + return p.name == s.name; + }) != pkgs.end ()) + ps.push_back (s.name.string ().c_str ()); } - // Collect packages. - // - if (all) + if (ps.empty ()) { - pkgs.clear (); + if (verb) + { + diag_record dr (info); + + dr << "skipping configuration " << *c; - for (const package_state& p: c->packages) - pkgs.push_back (p.name.string ().c_str ()); + if (c->packages.empty ()) + dr << info << "configuration is empty"; + else + dr << info << "none of specified packages initialized in this " + << "configuration"; + } + + continue; } // If we are printing multiple configurations, separate them with a @@ -107,12 +123,12 @@ namespace bdep first = false; } - // Pre-sync the configuration to avoid triggering the build system - // hook (see sync for details). + // Pre-sync the configuration to avoid triggering the build system hook + // (see sync for details). // cmd_sync (o, prj, c, strings () /* pkg_args */, true /* implicit */); - build (o, c, pkgs, cfg_vars); + build (o, c, ps, cfg_vars); } return 0; diff --git a/bdep/ci.cli b/bdep/ci.cli index 08d65cc..5f955ea 100644 --- a/bdep/ci.cli +++ b/bdep/ci.cli @@ -20,9 +20,9 @@ namespace bdep \c{\b{bdep ci} [] [] []} - \c{ = (\b{--directory}|\b{-d} )... | \n - = \b{--directory}|\b{-d} \n - = \b{@} | \b{--config}|\b{-c} } + \c{ = (\b{@} | \b{--config}|\b{-c} )... | \b{--all}|\b{-a}\n + = (\b{--directory}|\b{-d} )... | \n + = \b{--directory}|\b{-d} } \h|DESCRIPTION| @@ -31,10 +31,10 @@ namespace bdep If no project or package directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is used. If the specified directory is a project directory, - then all the packages initialized in the configuration are submitted. See - \l{bdep-projects-configs(1)} for details on specifying projects and - configurations. + configurations are used. If the specified directory is a project + directory, then all the packages initialized in the configurations are + submitted. See \l{bdep-projects-configs(1)} for details on specifying + projects and configurations. A CI request consists of the specified packages and their versions as well as the project's remote version control repository URL corresponding diff --git a/bdep/ci.cxx b/bdep/ci.cxx index 3b59a30..a4acd94 100644 --- a/bdep/ci.cxx +++ b/bdep/ci.cxx @@ -249,10 +249,10 @@ namespace bdep // If we are submitting the entire project, then we have two choices: we // can list all the packages in the project or we can only do so for - // packages that were initialized in the (specified) configuration(s?). + // packages that were initialized in the specified configurations. // // Note that other than getting the list of packages, we would only need - // the configuration to obtain their versions. Since we can only have one + // the configurations to obtain their versions. Since we can only have one // version for each package this is not strictly necessary but is sure a // good sanity check against local/remote mismatches. Also, it would be // nice to print the versions we are submitting in the prompt. @@ -262,10 +262,14 @@ namespace bdep // has configurations for subsets of packages or some such. And in the // future, who knows, we could have multi-project CI. // - // So, let's go with the configuration. Specifically, if packages were + // So, let's go with the configurations. Specifically, if packages were // explicitly specified, we verify they are initialized. Otherwise, we use - // the list of packages that are initialized in a configuration (single - // for now). + // the list of packages that are initialized in configurations. In both + // cases we also verify that for each package only one configuration, it + // is initialized in, is specified (while we currently don't need this + // restriction, this may change in the future if we decide to support + // archive-based CI or some such). + // // // Note also that no pre-sync is needed since we are only getting versions // (via the info meta-operation). @@ -277,51 +281,87 @@ namespace bdep const dir_path& prj (pp.project); - shared_ptr cfg; + configurations cfgs; { // Don't keep the database open longer than necessary. // database db (open (prj, trace)); transaction t (db.begin ()); - configurations cfgs (find_configurations (o, prj, t)); + cfgs = find_configurations (o, prj, t).first; t.commit (); - - if (cfgs.size () > 1) - fail << "multiple configurations specified for ci"; - - // If specified, verify packages are present in the configuration. - // - if (!pp.packages.empty ()) - verify_project_packages (pp, cfgs); - - cfg = move (cfgs[0]); } - // Collect package names and their versions. + // Collect package names, versions, and configurations used. // struct package { - package_name name; - standard_version version; + package_name name; + standard_version version; + shared_ptr config; }; vector pkgs; - auto add_package = [&o, &cfg, &pkgs] (package_name n) + // Add a package to the list, suppressing duplicates and verifying that it + // is initialized in only one configuration. + // + auto add_package = [&o, &pkgs] (package_name n, + shared_ptr c) { - standard_version v (package_version (o, cfg->path, n)); - pkgs.push_back (package {move (n), move (v)}); + auto i (find_if (pkgs.begin (), + pkgs.end (), + [&n] (const package& p) {return p.name == n;})); + + if (i != pkgs.end ()) + { + if (i->config == c) + return; + + fail << "package " << n << " is initialized in multiple specified " + << "configurations" << + info << *i->config << + info << *c; + } + + standard_version v (package_version (o, c->path, n)); + pkgs.push_back (package {move (n), move (v), move (c)}); }; if (pp.packages.empty ()) { - for (const package_state& p: cfg->packages) - add_package (p.name); + for (const shared_ptr& c: cfgs) + { + for (const package_state& p: c->packages) + add_package (p.name, c); + } } else { for (package_location& p: pp.packages) - add_package (p.name); + { + bool init (false); + + for (const shared_ptr& c: cfgs) + { + if (find_if (c->packages.begin (), + c->packages.end (), + [&p] (const package_state& s) + { + return p.name == s.name; + }) != c->packages.end ()) + { + // Add the package, but continue the loop to detect a potential + // configuration ambiguity. + // + add_package (p.name, c); + init = true; + } + } + + if (!init) + fail << "package " << p.name << " is not initialized in any " + << "configuration"; + } } // Extract the interactive mode configuration and breakpoint from the diff --git a/bdep/clean.cli b/bdep/clean.cli index 141d20a..6a2e140 100644 --- a/bdep/clean.cli +++ b/bdep/clean.cli @@ -41,9 +41,9 @@ namespace bdep If no project or package directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is assumed. See \l{bdep-projects-configs(1)} for details on - specifying projects and configurations. Optional \c{\i{cfg-var}...} are - the additional configuration variables to pass to the build system. + configurations are assumed. See \l{bdep-projects-configs(1)} for details + on specifying projects and configurations. Optional \c{\i{cfg-var}...} + are the additional configuration variables to pass to the build system. " } diff --git a/bdep/config.cli b/bdep/config.cli index 6d26432..a1cdba2 100644 --- a/bdep/config.cli +++ b/bdep/config.cli @@ -19,6 +19,7 @@ namespace bdep \c{\b{bdep config add} \ \ \ [] [] [\b{@}] \n \b{bdep config create} [] [] [\b{@}] []\n + \b{bdep config link} \ \ [] [] \n \b{bdep config list} \ \ [] [] [...]\n \b{bdep config move} \ \ [] [] \n \b{bdep config rename} [] [] \n @@ -77,23 +78,31 @@ namespace bdep $ bdep config create -- @gcc cc config.cxx=g++ # ../hello-gcc \ + A configuration also has a type that is specified with the + \cb{--config-type} option. If the type is not specified explicitly, + then \cb{target} is assumed. See \l{bpkg-cfg-create(1)} for + background on configuration types. + Unless the \cb{--no-default} option is specified, the first added or - created build configuration is designated as the default. Several - \cb{bdep} commands use such a configuration by default if no - configuration was specified explicitly (see + created build configuration of each type is designated as the + default. Several \cb{bdep} commands use such a configuration by + default if no configuration was specified explicitly (see \l{bdep-projects-configs(1)} for details). To make a subsequently - added configuration the default use the \cb{--default} option. - - The default build configuration is also designated as forwarded - unless the \cb{--no-forward} option is specified or another - configuration is already designated as forwarded. When a project is - initialized in a forwarded build configuration, its source directory - is configured to forward to this configuration (see \l{b(1)} for - details on forwarded configurations). To designate a non-default - configuration as forwarded use the \cb{--forward} option. Note also - that it is possible to have multiple forwarded configurations, - however, any given package within a project can only be initialized - in one such configuration. + added configuration the default use the \cb{--default} option. Note + also that in case of multiple default configurations any given + package within a project can only be initialized in one such + configuration. + + The default build configuration of each type is also designated as + forwarded unless the \cb{--no-forward} option is specified or another + configuration of this type is already designated as forwarded. When a + project is initialized in a forwarded build configuration, its source + directory is configured to forward to this configuration (see + \l{b(1)} for details on forwarded configurations). To designate a + non-default configuration as forwarded use the \cb{--forward} + option. Note also that it is possible to have multiple forwarded + configurations, however, any given package within a project can only + be initialized in one such configuration. Unless the \cb{--no-auto-sync} option is specified, an added or created build configuration will be automatically synchronized on @@ -102,6 +111,12 @@ namespace bdep configuration, then they must have a consistent auto-synchronization setting.| + \li|\cb{link} + + The \cb{link} subcommand links one build configuration with another + by executing the \l{bpkg-cfg-link(1)} command. See + \l{bpkg-cfg-create(1)} for background on linked configurations.| + \li|\cb{list} The \cb{list} subcommand prints the list of build configurations @@ -145,10 +160,11 @@ namespace bdep (\c{\b{--}[\b{no-}]\b{forward}}), and auto-synchronization (\c{\b{--}[\b{no-}]\b{auto-sync}}) flags. Note that changing any of these flags requires an explicit \l{bdep-sync(1)} command to take - effect. ||" + effect.||" bool add; bool create; + bool link; bool list; bool move; bool rename; @@ -164,7 +180,6 @@ namespace bdep "\h|CONFIG OPTIONS|" }; - " \h|DEFAULT OPTIONS FILES| diff --git a/bdep/config.cxx b/bdep/config.cxx index 8d33155..f06bb98 100644 --- a/bdep/config.cxx +++ b/bdep/config.cxx @@ -22,7 +22,7 @@ namespace bdep if (c->name) o << '@' << *c->name << ' '; - o << c->path << ' ' << *c->id; + o << c->path << ' ' << *c->id << ' ' << c->type; if (flags) { @@ -54,14 +54,15 @@ namespace bdep if (o.existing () && o.wipe ()) fail << "both --existing|-e and --wipe specified"; - return (o.default_ () ? "--default" : - o.no_default () ? "--no-default" : - o.forward () ? "--forward" : - o.no_forward () ? "--no-forward" : - o.auto_sync () ? "--auto-sync" : - o.no_auto_sync () ? "--no-auto-sync" : - o.existing () ? "--existing|-e" : - o.wipe () ? "--wipe" : nullptr); + return (o.config_type_specified () ? "--config-type" : + o.default_ () ? "--default" : + o.no_default () ? "--no-default" : + o.forward () ? "--forward" : + o.no_forward () ? "--no-forward" : + o.auto_sync () ? "--auto-sync" : + o.no_auto_sync () ? "--no-auto-sync" : + o.existing () ? "--existing|-e" : + o.wipe () ? "--wipe" : nullptr); } void @@ -137,12 +138,14 @@ namespace bdep } shared_ptr - cmd_config_add (const configuration_add_options& ao, + cmd_config_add (const common_options& co, + const configuration_add_options& ao, const dir_path& prj, const package_locations& pkgs, database& db, dir_path path, optional name, + optional type, optional id, const char* what) { @@ -161,8 +164,53 @@ namespace bdep verify_configuration_path (path, prj, pkgs); - optional rel_path; - try {rel_path = path.relative (prj);} catch (const invalid_path&) {} + // Use bpkg-cfg-info to query the configuration type, unless specified + // explicitly. + // + if (!type) + { + fdpipe pipe (open_pipe ()); // Text mode seems appropriate. + + process pr (start_bpkg (3, + co, + pipe /* stdout */, + 2 /* stderr */, + "cfg-info", + "-d", path)); + + // Shouldn't throw, unless something is severely damaged. + // + pipe.out.close (); + + bool io (false); + try + { + ifdstream is (move (pipe.in), fdstream_mode::skip, ifdstream::badbit); + + for (string l; !eof (getline (is, l)); ) + { + if (l.compare (0, 6, "type: ") == 0) + { + type = string (l, 6); + break; + } + } + + is.close (); // Detect errors. + + if (!type || type->empty ()) + fail << "invalid bpkg-cfg-info output: no configuration type"; + } + catch (const io_error&) + { + // Presumably the child process failed and issued diagnostics so let + // finish_bpkg() try to deal with that first. + // + io = true; + } + + finish_bpkg (co, pr, io); + } transaction t (db.begin ()); @@ -172,36 +220,43 @@ namespace bdep { using query = bdep::query; - // By default the first added configuration is the default. + // By default the first added for its type configuration is the default. // if (ao.default_ () || ao.no_default ()) def = ao.default_ () && !ao.no_default (); if (!def) - def = (db.query_value () == 0); + def = (db.query_value (query::type == *type) == 0); else if (*def) { - if (auto p = db.query_one (query::default_)) - fail << "configuration " << *p << " is already the default" << + if (auto p = db.query_one (query::default_ && + query::type == *type)) + fail << "configuration " << *p << " of type " << *type + << " is already the default" << info << "use 'bdep config set --no-default' to clear"; } // By default the default configuration is forwarded unless another is - // already forwarded. + // already forwarded for its configuration type. // if (ao.forward () || ao.no_forward ()) - fwd = ao.forward () && !ao.no_forward (); + fwd = ao.forward () && !ao.no_forward (); + // Note: there can be multiple forwarded configurations for a type. + // if (!fwd) - fwd = *def && db.query_one (query::forward) == nullptr; + fwd = *def && + db.query_value (query::forward && + query::type == *type) == 0; } shared_ptr r ( new configuration { id, name, + move (*type), path, - move (rel_path), + path.try_relative (prj), *def, *fwd, !ao.no_auto_sync (), @@ -258,6 +313,7 @@ namespace bdep dir_path path, const strings& args, optional name, + string type, optional id) { // Similar logic to *_add(). @@ -274,16 +330,22 @@ namespace bdep co, "create", "-d", path, + (name + ? strings ({"--name", *name}) + : strings ()), + "--type", type, (ao.existing () ? "--existing" : nullptr), (ao.wipe () ? "--wipe" : nullptr), args); - return cmd_config_add (ao, + return cmd_config_add (co, + ao, prj, package_locations {}, // Already verified. db, move (path), move (name), + move (type), id, ao.existing () ? "initialized" : "created"); } @@ -324,11 +386,13 @@ namespace bdep database db (open (prj, trace)); cmd_config_add (o, + o, prj, load_packages (prj, true /* allow_empty */), db, move (path), move (name), + nullopt, /* type */ move (id)); return 0; } @@ -382,11 +446,61 @@ namespace bdep move (path), cfg_args, move (name), + o.config_type (), move (id)); return 0; } static int + cmd_config_link (const cmd_config_options& o, cli::scanner&) + { + tracer trace ("config_link"); + + // Load project configurations. + // + configurations cfgs; + { + dir_path prj (find_project (o)); + database db (open (prj, trace)); + + transaction t (db.begin ()); + + cfgs = find_configurations (o, + prj, + t, + false /* fallback_default */, + true /* validate */).first; + + t.commit (); + } + + if (cfgs.size () != 2) + fail << "two configurations must be specified for config link"; + + const dir_path& cd (cfgs[0]->path); + const dir_path& ld (cfgs[1]->path); + + // Call bpkg to link the configurations. + // + // If possible, rebase the linked configuration directory path relative to + // the other configuration path. + // + run_bpkg (2, + o, + "cfg-link", + ld.try_relative (cd) ? "--relative" : nullptr, + "-d", cd, + ld); + + if (verb) + text << "linked configuration " << *cfgs[0] << " (" << cfgs[0]->type + << ") with configuration " << *cfgs[1] << " (" << cfgs[1]->type + << ")"; + + return 0; + } + + static int cmd_config_list (const cmd_config_options& o, cli::scanner&) { tracer trace ("config_list"); @@ -405,7 +519,7 @@ namespace bdep prj, t, false /* fallback_default */, - false /* validate */); + false /* validate */).first; } else { @@ -425,7 +539,6 @@ namespace bdep t.commit (); - for (const shared_ptr& c: cfgs) { //@@ TODO: use tabular layout facility when ready. @@ -467,7 +580,7 @@ namespace bdep fail << "invalid configuration directory '" << a << "'"; } - try {rel_path = path.relative (prj);} catch (const invalid_path&) {} + rel_path = path.try_relative (prj); } database db (open (prj, trace)); @@ -480,7 +593,7 @@ namespace bdep prj, t, false /* fallback_default */, - false /* validate */)); + false /* validate */).first); if (cfgs.size () > 1) fail << "multiple configurations specified for config move"; @@ -500,6 +613,10 @@ namespace bdep // Save the old path for diagnostics. // + // @@ We should probably also adjust explicit and implicit links in the + // respective bpkg configurations, if/when bpkg provides the required + // API. + // c->path.swap (path); c->relative_path = move (rel_path); @@ -567,7 +684,7 @@ namespace bdep prj, t, false /* fallback_default */, - false /* validate */)); + false /* validate */).first); if (cfgs.size () > 1) fail << "multiple configurations specified for config rename"; @@ -625,7 +742,7 @@ namespace bdep prj, t, false /* fallback_default */, - false /* validate */)); + false /* validate */).first); for (const shared_ptr& c: cfgs) { @@ -683,19 +800,46 @@ namespace bdep prj, t, false /* fallback_default */, - false /* validate */)); + false /* validate */).first); for (const shared_ptr& c: cfgs) { using query = bdep::query; + // Verify that there is no other default or forwarded configuration with + // the same package as us. + // + auto verify = [&c, &db] (const query& q, const char* what) + { + for (const shared_ptr& o: + pointer_result (db.query (q))) + { + auto i (find_first_of ( + o->packages.begin (), o->packages.end (), + c->packages.begin (), c->packages.end (), + [] (const package_state& x, const package_state& y) + { + return x.name == y.name; + })); + + if (i != o->packages.end ()) + fail << "configuration " << *o << " is also " << what << " and " + << "also has package " << i->name << " initialized" << + info << "while updating configuration " << *c; + } + }; + if (d) { if (*d && !c->default_) { - if (auto p = db.query_one (query::default_)) - fail << "configuration " << *p << " is already the default" << + if (auto p = db.query_one (query::default_ && + query::type == c->type)) + fail << "configuration " << *p << " of type " << p->type + << " is already the default" << info << "while updating configuration " << *c; + + verify (query::default_, "default"); } c->default_ = *d; @@ -704,27 +848,7 @@ namespace bdep if (f) { if (*f && !c->forward) - { - // Make sure there are no other forwarded configurations with the - // same package as us. - // - for (const shared_ptr& o: - pointer_result (db.query (query::forward))) - { - auto i (find_first_of ( - o->packages.begin (), o->packages.end (), - c->packages.begin (), c->packages.end (), - [] (const package_state& x, const package_state& y) - { - return x.name == y.name; - })); - - if (i != o->packages.end ()) - fail << "configuration " << *o << " is also forwarded and " - << "also has package " << i->name << " initialized" << - info << "while updating configuration " << *c; - } - } + verify (query::forward, "forwarded"); c->forward = *f; } @@ -768,6 +892,9 @@ namespace bdep if (!c.add () && !c.create () && !c.set ()) fail << n << " not valid for this subcommand"; + if (o.config_type_specified () && !c.create ()) + fail << "--config-type is not valid for this subcommand"; + if (o.existing () && !c.create ()) fail << "--existing|-e is not valid for this subcommand"; @@ -787,6 +914,7 @@ namespace bdep // if (c.add ()) return cmd_config_add (o, scan); if (c.create ()) return cmd_config_create (o, scan); + if (c.link ()) return cmd_config_link (o, scan); if (c.list ()) return cmd_config_list (o, scan); if (c.move ()) return cmd_config_move (o, scan); if (c.rename ()) return cmd_config_rename (o, scan); diff --git a/bdep/config.hxx b/bdep/config.hxx index 049f030..111ce15 100644 --- a/bdep/config.hxx +++ b/bdep/config.hxx @@ -12,13 +12,17 @@ namespace bdep { + // If type is nullopt, then query the bpkg configuration type. + // shared_ptr - cmd_config_add (const configuration_add_options&, + cmd_config_add (const common_options&, + const configuration_add_options&, const dir_path& prj, const package_locations&, database&, dir_path path, optional name, + optional type, optional id = nullopt, const char* what = "added"); @@ -31,6 +35,7 @@ namespace bdep dir_path path, const strings& args, optional name, + string type, optional id = nullopt); int diff --git a/bdep/database.cxx b/bdep/database.cxx index 5e0aea5..064f9ed 100644 --- a/bdep/database.cxx +++ b/bdep/database.cxx @@ -8,6 +8,8 @@ #include +#include +#include #include #include @@ -18,6 +20,22 @@ namespace bdep using namespace odb::sqlite; using odb::schema_catalog; + // Register the data migration functions. + // + template + using migration_entry = odb::data_migration_entry; + + static const migration_entry<2> + migrate_v2 ([] (odb::database& db) + { + for (const shared_ptr& c: + pointer_result (db.query ())) + { + c->type = "target"; + db.update (c); + } + }); + database open (const dir_path& d, tracer& tr, bool create) { diff --git a/bdep/deinit.cli b/bdep/deinit.cli index 6aad32c..e78cdbe 100644 --- a/bdep/deinit.cli +++ b/bdep/deinit.cli @@ -32,8 +32,8 @@ namespace bdep If no project directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is assumed. See \l{bdep-projects-configs(1)} for details on - specifying projects and configurations. + configurations are assumed. See \l{bdep-projects-configs(1)} for details + on specifying projects and configurations. " } diff --git a/bdep/deinit.cxx b/bdep/deinit.cxx index 598efd5..0018aaf 100644 --- a/bdep/deinit.cxx +++ b/bdep/deinit.cxx @@ -113,32 +113,33 @@ namespace bdep configurations cfgs; { transaction t (db.begin ()); - cfgs = find_configurations (o, - prj, - t, - true /* fallback_default */, - !force /* validate */); + pair cs ( + find_configurations (o, + prj, + t, + true /* fallback_default */, + !force /* validate */)); t.commit (); - } - // If specified, verify packages are present in each configuration. - // - if (!pp.packages.empty ()) - verify_project_packages (pp, cfgs); + // If specified, verify packages are present in at least one + // configuration. + // + if (!pp.packages.empty ()) + verify_project_packages (pp, cs); + + cfgs = move (cs.first); + } // If no packages were explicitly specified, then we deinitalize all that - // have been initialized in each configuration. + // have been initialized in each configuration. Otherwise, we deinitalize + // only specified packages initialized in the (specified) configurations. // - strings pkgs; + const package_locations& pkgs (pp.packages); - bool all (pp.packages.empty ()); - if (!all) - { - for (const package_location& p: pp.packages) - pkgs.push_back (p.name.string ()); - } + bool all (pkgs.empty ()); - // Deinitialize in each configuration skipping empty ones. + // Deinitialize in each configuration, skipping those where no packages + // needs to be deinitialized. // // We do each configuration in a separate transaction so that our state // reflects the bpkg configuration as closely as possible. @@ -146,10 +147,36 @@ namespace bdep bool first (true); for (const shared_ptr& c: cfgs) { - if (c->packages.empty ()) + // Collect packages to deinitialize. + // + strings ps; + + for (const package_state& s: c->packages) + { + if (all || + find_if (pkgs.begin (), + pkgs.end (), + [&s] (const package_location& p) + { + return p.name == s.name; + }) != pkgs.end ()) + ps.push_back (s.name.string ()); + } + + if (ps.empty ()) { if (verb) - info << "skipping empty configuration " << *c; + { + diag_record dr (info); + + dr << "skipping configuration " << *c; + + if (c->packages.empty ()) + dr << info << "configuration is empty"; + else + dr << info << "none of specified packages initialized in this " + << "configuration"; + } continue; } @@ -167,41 +194,33 @@ namespace bdep transaction t (db.begin ()); - // Collect packages to drop and remove them from the configuration. + // Remove collected packages from the configuration. // - if (all) - { - pkgs.clear (); - - for (const package_state& p: c->packages) - pkgs.push_back (p.name.string ()); - } - c->packages.erase ( remove_if (c->packages.begin (), c->packages.end (), - [&pkgs] (const package_state& p) + [&ps] (const package_state& p) { - return find_if (pkgs.begin (), - pkgs.end (), + return find_if (ps.begin (), + ps.end (), [&p] (const string& n) { return p.name == n; - }) != pkgs.end (); + }) != ps.end (); }), c->packages.end ()); // If we are deinitializing multiple packages, print their names. // - if (verb && pkgs.size () > 1) + if (verb && ps.size () > 1) { - for (const string& n: pkgs) + for (const string& n: ps) text << "deinitializing package " << n; } // The same story as in init with regard to the state update order. // - cmd_deinit (o, prj, c, pkgs); + cmd_deinit (o, prj, c, ps); db.update (c); t.commit (); diff --git a/bdep/fetch.cli b/bdep/fetch.cli index 4349bdd..bfd9c53 100644 --- a/bdep/fetch.cli +++ b/bdep/fetch.cli @@ -29,8 +29,8 @@ namespace bdep If no project or package directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is assumed. See \l{bdep-projects-configs(1)} for details on - specifying projects and configurations. + configurations are assumed. See \l{bdep-projects-configs(1)} for details + on specifying projects and configurations. If the \cb{--full|-F} option is specified, then instead \cb{fetch} performs a full re-fetch of all the repositories added to the diff --git a/bdep/fetch.cxx b/bdep/fetch.cxx index 93a727f..96bfd4b 100644 --- a/bdep/fetch.cxx +++ b/bdep/fetch.cxx @@ -40,7 +40,7 @@ namespace bdep database db (open (prj, trace)); transaction t (db.begin ()); - cfgs = find_configurations (o, prj, t); + cfgs = find_configurations (o, prj, t).first; t.commit (); } diff --git a/bdep/init.cli b/bdep/init.cli index c8616c3..658c81e 100644 --- a/bdep/init.cli +++ b/bdep/init.cli @@ -42,10 +42,11 @@ namespace bdep If no project directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is assumed. See \l{bdep-projects-configs(1)} for details on - specifying projects and configurations. Optional are the - additional dependency packages and/or configuration variables to pass - to the underlying \l{bpkg-pkg-build(1)} command. + configuration is assumed (failing if multiple default configurations are + present). See \l{bdep-projects-configs(1)} for details on specifying + projects and configurations. Optional are the additional + dependency packages and/or configuration variables to pass to the + underlying \l{bpkg-pkg-build(1)} command. The second form (\cb{--empty} is specified) initializes an empty project database that can later be used to first add build configurations diff --git a/bdep/init.cxx b/bdep/init.cxx index a3f0859..2dbc1bb 100644 --- a/bdep/init.cxx +++ b/bdep/init.cxx @@ -53,7 +53,7 @@ namespace bdep const dir_path& cfg, const strings& args, bool ca, - bool cc) + optional cc) { const char* m (!ca ? "--config-create" : !cc ? "--config-add" : nullptr); @@ -66,8 +66,25 @@ namespace bdep cmd_config_validate_add (o, m, nm, id); return ca - ? cmd_config_add ( ao, prj, ps, db, cfg, move (nm), move (id)) - : cmd_config_create (o, ao, prj, ps, db, cfg, args, move (nm), move (id)); + ? cmd_config_add (o, + ao, + prj, + ps, + db, + cfg, + move (nm), + nullopt /* type */, + move (id)) + : cmd_config_create (o, + ao, + prj, + ps, + db, + cfg, + args, + move (nm), + move (*cc), + move (id)); } void @@ -79,6 +96,91 @@ namespace bdep const strings& pkg_args, bool sync) { + // Return true if a package is initialized in the specified configuration. + // + auto initialized = [] (const package_location& p, + const shared_ptr& c) + { + return find_if (c->packages.begin (), + c->packages.end (), + [&p] (const package_state& s) + { + return p.name == s.name; + }) != c->packages.end (); + }; + + // Verify that as a result of this operation, none of the packages will be + // initialized in multiple default or forwarded configurations and fail if + // that's not the case. + // + // Note that we perform verification in a separate loop to make sure that + // no actual bpkg commands are executed and no database changes are + // committed by the time of potential failure. + // + { + transaction t (db.begin ()); + + // For the sake of simplicity, add packages to the configuration + // objects, updating their state in the database (so that the changes + // are seen by the queries) and rollback the transaction in the + // end. Note that by doing so, we can end up modifying passed + // configuration objects and then failing, which is probably ok. + // + for (const shared_ptr& c: cfgs) + { + for (const package_location& p: pkgs) + { + // Skip the package if it is already initialized in this + // configuration. + // + if (initialized (p, c)) + continue; + + // Verify that there is no other default or forwarded configuration + // that also has this package. + // + using query = bdep::query; + + auto verify = [&c, &p, &db, &initialized] (const query& q, + const char* what) + { + for (const shared_ptr& o: + pointer_result (db.query (q))) + { + if (initialized (p, o)) + { + // Differ, since the package is not initialized in `c` (see + // above). + // + assert (o->id != c->id); + + fail << what << " configuration " << *o << " also has package " + << p.name << " initialized" << + info << "while initializing in " << what << " configuration " + << *c << + info << "specify packages and configurations explicitly"; + } + } + }; + + if (c->default_) + verify (query::default_, "default"); + + if (c->forward) + verify (query::forward, "forwarded"); + + c->packages.push_back (package_state {p.name}); + } + + db.update (c); + } + + t.rollback (); + } + + // Now, execute the actual bpkg commands and update configurations for + // real. + // // We do each configuration in a separate transaction so that our state // reflects the bpkg configuration as closely as possible. // @@ -108,14 +210,15 @@ namespace bdep transaction t (db.begin ()); + // Reload the configuration object that could have been changed during + // verification. This is required for skipping the already initialized + // packages. + // + db.reload (*c); + for (const package_location& p: pkgs) { - if (find_if (c->packages.begin (), - c->packages.end (), - [&p] (const package_state& s) - { - return p.name == s.name; - }) != c->packages.end ()) + if (initialized (p, c)) { if (verb) info << "package " << p.name << " is already initialized"; @@ -123,38 +226,11 @@ namespace bdep continue; } - // If this configuration is forwarded, verify there is no other - // forwarded configuration that also has this package. - // - if (c->forward) - { - using query = bdep::query; - - for (const shared_ptr& o: - pointer_result (db.query (query::forward))) - { - if (o == c) - continue; - - if (find_if (o->packages.begin (), - o->packages.end (), - [&p] (const package_state& s) - { - return p.name == s.name; - }) != o->packages.end ()) - { - fail << "forwarded configuration " << *o << " also has package " - << p.name << " initialized" << - info << "while initializing in forwarded configuration " << *c; - } - } - } - // If we are initializing multiple packages or there will be no sync, // print their names. // if (verb && (pkgs.size () > 1 || !sync)) - text << "initializing package " << p.name;; + text << "initializing package " << p.name; c->packages.push_back (package_state {p.name}); } @@ -190,7 +266,12 @@ namespace bdep tracer trace ("init"); bool ca (o.config_add_specified ()); - bool cc (o.config_create_specified ()); + + // Type of configuration being created, if --config-create is specified. + // + optional cc (o.config_create_specified () + ? o.config_type () + : optional ()); if (o.empty ()) { @@ -204,6 +285,9 @@ namespace bdep if (!ca && !cc) fail << n << " specified without --config-(add|create)"; + if (o.config_type_specified () && !cc) + fail << "--config-type specified without --config-create"; + if (o.existing () && !cc) fail << "--existing|-e specified without --config-create"; @@ -243,11 +327,6 @@ namespace bdep configurations cfgs; { - // Make sure everyone refers to the same objects across all the - // transactions. - // - session s; - // --config-add/create // if (ca || cc) @@ -272,7 +351,7 @@ namespace bdep ca ? o.config_add () : o.config_create (), cfg_args, ca, - cc)); + move (cc))); } else { @@ -280,7 +359,7 @@ namespace bdep // wants us to use. // transaction t (db.begin ()); - cfgs = find_configurations (o, prj, t); + cfgs = find_configurations (o, prj, t).first; t.commit (); } } diff --git a/bdep/init.hxx b/bdep/init.hxx index 6a2afd2..fff5f84 100644 --- a/bdep/init.hxx +++ b/bdep/init.hxx @@ -23,7 +23,7 @@ namespace bdep const dir_path& cfg, const strings& cfg_args, bool config_add_specified, - bool config_create_specified); + optional config_create_type); // Initialize each package in each configuration skipping those that are // already initialized. Then synchronize each configuration unless sync diff --git a/bdep/new.cxx b/bdep/new.cxx index b512497..5998630 100644 --- a/bdep/new.cxx +++ b/bdep/new.cxx @@ -242,7 +242,12 @@ cmd_new (cmd_new_options&& o, cli::group_scanner& args) // Validate options. // bool ca (o.config_add_specified ()); - bool cc (o.config_create_specified ()); + + // Type of configuration being created, if --config-create is specified. + // + optional cc (o.config_create_specified () + ? o.config_type () + : optional ()); if (o.subdirectory ()) fail << "--subdirectory was renamed to --source"; @@ -271,6 +276,9 @@ cmd_new (cmd_new_options&& o, cli::group_scanner& args) if (!ca && !cc) fail << n << " specified without --config-(add|create)"; + if (o.config_type_specified () && !cc) + fail << "--config-type specified without --config-create"; + if (o.existing () && !cc) fail << "--existing|-e specified without --config-create"; @@ -3159,7 +3167,7 @@ cmd_new (cmd_new_options&& o, cli::group_scanner& args) ca ? o.config_add () : o.config_create (), cfg_args, ca, - cc)}; + move (cc))}; cmd_init (o, prj, db, cfgs, pkgs, strings () /* pkg_args */); } diff --git a/bdep/project.cli b/bdep/project.cli index 1b6c7aa..17ef72e 100644 --- a/bdep/project.cli +++ b/bdep/project.cli @@ -11,6 +11,14 @@ namespace bdep // class configuration_add_options { + string --config-type = "target" + { + "", + "The type of the configuration being created. By default, configuration + of type \cb{target} is created. See \l{bpkg-cfg-create(1)} for + background on configuration types." + } + bool --default { "Make the added or created configuration the default." diff --git a/bdep/project.cxx b/bdep/project.cxx index 7190045..ace8e3b 100644 --- a/bdep/project.cxx +++ b/bdep/project.cxx @@ -16,7 +16,7 @@ using namespace butl; namespace bdep { - configurations + pair find_configurations (const project_options& po, const dir_path& prj, transaction& t, @@ -24,6 +24,7 @@ namespace bdep bool validate) { configurations r; + bool fallback (false); // Weed out duplicates. // @@ -99,12 +100,16 @@ namespace bdep { if (fallback_default) { - if (auto c = db.query_one (query::default_)) + for (shared_ptr c: + pointer_result (db.query (query::default_))) add (move (c)); - else + + if (r.empty ()) fail << "no default configuration in project " << prj << info << "use (@ | --config|-c | --all|-a) to " - << "specify configuration explicitly"; + << "specify configuration explicitly"; + + fallback = true; } else fail << "no configurations specified"; @@ -123,7 +128,7 @@ namespace bdep } } - return r; + return make_pair (move (r), fallback); } project_package @@ -400,23 +405,39 @@ namespace bdep void verify_project_packages (const project_packages& pp, - const configurations& cfgs) + const pair& cfgs) { - for (const shared_ptr& c: cfgs) + for (const package_location& p: pp.packages) { - for (const package_location& p: pp.packages) + bool init (false); + + for (const shared_ptr& c: cfgs.first) { if (find_if (c->packages.begin (), c->packages.end (), [&p] (const package_state& s) { return p.name == s.name; - }) == c->packages.end ()) + }) != c->packages.end ()) { - fail << "package " << p.name << " is not initialized " - << "in configuration " << *c; + init = true; + break; } } + + if (!init) + { + diag_record dr (fail); + + dr << "package " << p.name << " is not initialized in "; + + if (cfgs.second) + dr << "any default configuration(s)"; + else if (cfgs.first.size () == 1) + dr << "configuration " << *cfgs.first.front (); + else + dr << "any specified configurations"; + } } } diff --git a/bdep/project.hxx b/bdep/project.hxx index 86b833f..5c6be87 100644 --- a/bdep/project.hxx +++ b/bdep/project.hxx @@ -15,7 +15,11 @@ #include -#pragma db model version(1, 1, closed) +// Used by the data migration entries. +// +#define DB_SCHEMA_VERSION_BASE 1 + +#pragma db model version(DB_SCHEMA_VERSION_BASE, 2, closed) // Prevent assert() macro expansion in get/set expressions. This should appear // after all #include directives since the assert() macro is redefined in each @@ -85,6 +89,7 @@ namespace bdep // optional_uint64_t id; optional_string name; + string type; dir_path path; optional_dir_path relative_path; @@ -122,6 +127,7 @@ namespace bdep // configuration (optional_uint64_t i, optional_string n, + string t, dir_path p, optional_dir_path rp, bool d, @@ -130,6 +136,7 @@ namespace bdep vector ps) : id (i), name (move (n)), + type (move (t)), path (move (p)), relative_path (move (rp)), default_ (d), @@ -166,12 +173,15 @@ namespace bdep // Given the project directory, database, and options resolve all the // mentioned configurations or, unless fallback_default is false, find the - // default configuration if none were mentioned. Unless validate is false, + // default configurations if none were mentioned. Unless validate is false, // also validate that the configuration directories still exist. // + // Besides configurations, also return an indication if they are retrieved + // as a fallback to default configurations (true if that's the case). + // using configurations = vector>; - configurations + pair find_configurations (const project_options&, const dir_path& prj, transaction&, @@ -255,10 +265,14 @@ namespace bdep return find_project_packages (o, true /* ignore_packages */).project; } - // Verify all the packages are present in all the configurations. + // Verify that each package is present in at least one configuration. + // + // Note that the default configurations fallback indication (see above) is + // only used for diagnostics. // void - verify_project_packages (const project_packages&, const configurations&); + verify_project_packages (const project_packages&, + const pair&); // Determine the version of a package in the specified package (first // version) or configuration (second version) directory. diff --git a/bdep/project.xml b/bdep/project.xml index e69af8b..805d741 100644 --- a/bdep/project.xml +++ b/bdep/project.xml @@ -1,4 +1,10 @@ + + + + + + diff --git a/bdep/projects-configs.cli b/bdep/projects-configs.cli index 72b9736..a5c5808 100644 --- a/bdep/projects-configs.cli +++ b/bdep/projects-configs.cli @@ -58,11 +58,12 @@ include ; \cb{packages.manifest}). A project managed by \cb{bdep} has one or more associated build - configurations (see \l{bdep-config(1)} for details). One of these - configurations can be designated as the default and used if no configuration - is specified explicitly. So, for example, running \cb{status} without any - arguments in the project directory will show the status of all the project - packages initialized in the default configuration. + configurations (see \l{bdep-config(1)} for details). Some of these + configurations, one per the configuration type, can be designated as default + and used if no configuration is specified explicitly. So, for example, + running \cb{status} without any arguments in the project directory will show + the status of all the project packages initialized in the default + configurations. An associated build configuration can be assigned a name in which case we can specify it using the \c{\b{@}\i{cfg-name}} notation. For example: diff --git a/bdep/publish.cli b/bdep/publish.cli index d8ecb82..42504ac 100644 --- a/bdep/publish.cli +++ b/bdep/publish.cli @@ -23,7 +23,7 @@ namespace bdep \c{ = (\b{--directory}|\b{-d} )... | \n = \b{--directory}|\b{-d} \n - = \b{@} | \b{--config}|\b{-c} | \b{--forward}} + = (\b{@} | \b{--config}|\b{-c} )... | \b{--all}|\b{-a} | \b{--forward}} \h|DESCRIPTION| @@ -32,7 +32,7 @@ namespace bdep If no project or package directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is used to prepare the package distributions. If the + configurations are used to prepare the package distributions. If the specified directory is a project directory, then all the packages in the project are published. See \l{bdep-projects-configs(1)} for details on specifying projects and configurations. diff --git a/bdep/publish.cxx b/bdep/publish.cxx index d39cbcc..75c38d8 100644 --- a/bdep/publish.cxx +++ b/bdep/publish.cxx @@ -48,16 +48,18 @@ namespace bdep info << "use --control to specify explicitly" << endf; } - // If cfg is empty, then use each package's (forwarded) source directory. + // Distribute packages in the specified (per-package) directories. // static int cmd_publish (const cmd_publish_options& o, const dir_path& prj, - const dir_path& cfg, - package_locations&& pkg_locs) + package_locations&& pkg_locs, + dir_paths&& dist_dirs) { using bpkg::package_manifest; + assert (pkg_locs.size () == dist_dirs.size ()); // Parallel vectors. + const url& repo (o.repository ()); // Control repository URL. @@ -125,7 +127,7 @@ namespace bdep struct package { package_name name; - dir_path path; + dir_path dist_dir; standard_version version; package_name project; string section; // alpha|beta|stable (or --section) @@ -158,14 +160,14 @@ namespace bdep info << "use --force=uncommitted to publish anyway"; } - for (package_location& pl: pkg_locs) + for (size_t i (0); i != pkg_locs.size (); ++i) { - package_name n (move (pl.name)); - package_name p (pl.project ? move (*pl.project) : n); + package_location& pl (pkg_locs[i]); - standard_version v (cfg.empty () - ? package_version (o, prj / pl.path) - : package_version (o, cfg, n)); + package_name n (move (pl.name)); + package_name p (pl.project ? move (*pl.project) : n); + dir_path d (move (dist_dirs[i])); + standard_version v (package_version (o, d)); // Should we allow publishing snapshots and, if so, to which section? // For example, is it correct to consider a "between betas" snapshot a @@ -204,7 +206,7 @@ namespace bdep v.beta () ? "beta" : "stable"); pkgs.push_back (package {move (n), - move (pl.path), + move (d), move (v), move (p), move (s), @@ -269,13 +271,10 @@ namespace bdep // build2's version module by default does not allow distribution of // uncommitted projects. // - dir_path d (cfg.empty () - ? prj / p.path - : dir_path (cfg) /= p.name.string ()); run_b ( o, "dist:", - "'" + d.representation () + "'", + "'" + p.dist_dir.representation () + "'", "config.dist.root='" + dr.representation () + "'", "config.dist.archives=tar.gz", "config.dist.checksums=sha256", @@ -858,16 +857,17 @@ namespace bdep const dir_path& prj (pp.project); - // Unless we are using the forwarded configurations, we need a single - // configuration to prepare package distributions. + // Collect directories to distribute the packages in. In the forward mode + // the packages are distributed in the package's (forwarded) source + // directories and in their configuration directories otherwise. // - dir_path cfg_dir; + dir_paths dist_dirs; if (o.forward ()) { // Note: in this case we don't even open the database. // - dir_paths cfgs; + dir_paths cfgs; // Configuration directories to sync. for (const package_location& pl: pp.packages) { @@ -883,6 +883,8 @@ namespace bdep // (pi.out_root /= pi.amalgamation).normalize (); + dist_dirs.push_back (move (d)); + if (find (cfgs.begin (), cfgs.end (), pi.out_root) == cfgs.end ()) cfgs.push_back (move (pi.out_root)); } @@ -895,31 +897,66 @@ namespace bdep } else { - shared_ptr cfg; + configurations cfgs; { // Don't keep the database open longer than necessary. // database db (open (prj, trace)); transaction t (db.begin ()); - configurations cfgs (find_configurations (o, prj, t)); + cfgs = find_configurations (o, prj, t).first; t.commit (); + } - if (cfgs.size () > 1) - fail << "multiple configurations specified for publish"; + // Configurations to sync. + // + // We could probably unify syncing configuration directories with the + // forward mode, however configuration name-based progress indication + // feels preferable for the common case. + // + configurations scs; - // Verify packages are present in the configuration. - // - verify_project_packages (pp, cfgs); + // Besides collecting the package directories and configurations to + // sync, also verify that for each package being published only one + // configuration, it is initialized in, is specified. + // + for (const package_location& p: pp.packages) + { + shared_ptr pc; - cfg = move (cfgs[0]); - } + for (const shared_ptr& c: cfgs) + { + if (find_if (c->packages.begin (), + c->packages.end (), + [&p] (const package_state& s) + { + return p.name == s.name; + }) != c->packages.end ()) + { + if (pc != nullptr) + fail << "package " << p.name << " is initialized in multiple " + << "specified configurations" << + info << *pc << + info << *c; + + pc = c; + } + } + + if (pc == nullptr) + fail << "package " << p.name << " is not initialized in any " + << "configuration"; - cmd_sync (o, prj, cfg, strings () /* pkg_args */, true /* implicit */); + dist_dirs.push_back (dir_path (pc->path) /= p.name.string ()); + + if (find (scs.begin (), scs.end (), pc) == scs.end ()) + scs.push_back (move (pc)); + } - cfg_dir = cfg->path; + for (const shared_ptr& c: scs) + cmd_sync (o, prj, c, strings () /* pkg_args */, true /* implicit */); } - return cmd_publish (o, prj, cfg_dir, move (pp.packages)); + return cmd_publish (o, prj, move (pp.packages), move (dist_dirs)); } } diff --git a/bdep/status.cli b/bdep/status.cli index 39688cb..38e70e0 100644 --- a/bdep/status.cli +++ b/bdep/status.cli @@ -32,8 +32,8 @@ namespace bdep If no project or package directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is assumed. See \l{bdep-projects-configs(1)} for details on - specifying projects and configurations. + configurations are assumed. See \l{bdep-projects-configs(1)} for details + on specifying projects and configurations. If no arguments are specified, then \cb{status} prints the status of the project's packages. Otherwise, the status of the specified diff --git a/bdep/status.cxx b/bdep/status.cxx index 2e60b29..fc07698 100644 --- a/bdep/status.cxx +++ b/bdep/status.cxx @@ -50,34 +50,6 @@ namespace bdep pkgs); } - static void - cmd_status (const cmd_status_options& o, - const dir_path& prj, - const shared_ptr& c, - const package_locations& ps, - bool fetch) - { - assert (!c->packages.empty ()); - - // If no packages were explicitly specified, then we print the status for - // all that have been initialized in the configuration. - // - strings pkgs; - - if (ps.empty ()) - { - for (const package_state& p: c->packages) - pkgs.push_back (p.name.string ()); - } - else - { - for (const package_location& p: ps) - pkgs.push_back (p.name.string ()); - } - - cmd_status (o, prj, c->path, pkgs, fetch); - } - int cmd_status (const cmd_status_options& o, cli::scanner& args) { @@ -103,16 +75,23 @@ namespace bdep database db (open (prj, trace)); - transaction t (db.begin ()); - configurations cfgs (find_configurations (o, prj, t)); - t.commit (); + configurations cfgs; + { + transaction t (db.begin ()); + pair cs (find_configurations (o, prj, t)); + t.commit (); - // If specified, verify packages are present in each configuration. - // - if (!pp.packages.empty ()) - verify_project_packages (pp, cfgs); + // If specified, verify packages are present in at least one + // configuration. + // + if (!pp.packages.empty ()) + verify_project_packages (pp, cs); + + cfgs = move (cs.first); + } - // Print status in each configuration skipping empty ones. + // Print status in each configuration, skipping those where no package + // statuses needs to be printed. // bool first (true); for (const shared_ptr& c: cfgs) @@ -120,11 +99,50 @@ namespace bdep if (c->packages.empty ()) { if (verb) - info << "skipping empty configuration " << *c; + info << "skipping configuration " << *c << + info << "configuration is empty"; continue; } + // Collect the packages to print, unless the dependency packages are + // specified. + // + // If no packages were explicitly specified, then we print the status + // for all that have been initialized in the configuration. Otherwise, + // only for specified packages initialized in the (specified) + // configurations. + // + strings pkgs; + + if (dep_pkgs.empty ()) + { + const package_locations& ps (pp.packages); + bool all (ps.empty ()); + + for (const package_state& s: c->packages) + { + if (all || + find_if (ps.begin (), + ps.end (), + [&s] (const package_location& p) + { + return p.name == s.name; + }) != ps.end ()) + pkgs.push_back (s.name.string ()); + } + + if (pkgs.empty ()) + { + if (verb) + info << "skipping configuration " << *c << + info << "none of specified packages initialized in this " + << "configuration"; + + continue; + } + } + // If we are printing multiple configurations, separate them with a // blank line and print the configuration name/directory. // @@ -141,10 +159,12 @@ namespace bdep if (fetch) cmd_fetch (o, prj, c, o.fetch_full ()); - if (dep_pkgs.empty ()) - cmd_status (o, prj, c, pp.packages, !fetch); - else - cmd_status (o, prj, c->path, dep_pkgs, !fetch); + // Status for either packages or their dependencies must be printed, but + // not for both. + // + assert (pkgs.empty () == !dep_pkgs.empty ()); + + cmd_status (o, prj, c->path, !pkgs.empty () ? pkgs : dep_pkgs, !fetch); } return 0; diff --git a/bdep/sync.cli b/bdep/sync.cli index 8f806b6..07c46a2 100644 --- a/bdep/sync.cli +++ b/bdep/sync.cli @@ -41,8 +41,8 @@ namespace bdep If no project or package directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is assumed. See \l{bdep-projects-configs(1)} for details on - specifying projects and configurations. Optional are the + configurations are assumed. See \l{bdep-projects-configs(1)} for details + on specifying projects and configurations. Optional are the additional dependency packages and/or configuration variables to pass to the underlying \l{bpkg-pkg-build(1)} command. diff --git a/bdep/sync.cxx b/bdep/sync.cxx index d47cd6c..0d5132f 100644 --- a/bdep/sync.cxx +++ b/bdep/sync.cxx @@ -188,7 +188,7 @@ namespace bdep // 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. + // upgrading specific dependency packages. Otherwise -- project packages. // static void cmd_sync (const common_options& co, @@ -743,12 +743,14 @@ namespace bdep if (o.directory_specified () || !o.implicit ()) { // We could be running from a package directory (or the user specified - // one 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 is false). + // one with -d) that has not been init'ed in this configuration. This, + // however, doesn't really matter since the specified packages are only + // used to determine which configuration package dependencies needs to + // be ugraded (see cmd_sync() for details). + // + // 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 + // is false). // project_packages pp ( find_project_packages (o, @@ -761,21 +763,24 @@ namespace bdep // Load project configurations. // + pair cs; { database db (open (pp.project, trace)); transaction t (db.begin ()); - cfgs = find_configurations (o, pp.project, t); + cs = find_configurations (o, pp.project, t); t.commit (); } - // If specified, verify packages are present in each configuration. + // If specified, verify packages are present in at least one + // configuration. // if (!pp.packages.empty ()) - verify_project_packages (pp, cfgs); + verify_project_packages (pp, cs); prj = move (pp.project); prj_pkgs = move (pp.packages); + cfgs = move (cs.first); } else { @@ -838,7 +843,8 @@ namespace bdep if (c != nullptr && c->packages.empty ()) { if (verb) - info << "skipping empty configuration " << *c; + info << "skipping configuration " << *c << + info << "configuration is empty"; continue; } @@ -857,6 +863,11 @@ namespace bdep if (!dep_pkgs.empty ()) { + // We ignore the project packages if the dependencies are specified + // explicitly (see the above find_project_packages() call). + // + assert (prj_pkgs.empty ()); + // The third form: upgrade of the specified dependencies. // // Only prompt if upgrading their dependencies. @@ -873,7 +884,7 @@ namespace bdep !o.patch (), // Upgrade by default unless patch requested. (o.recursive () ? optional (true) : o.immediate () ? optional (false) : nullopt), - prj_pkgs, + package_locations () /* prj_pkgs */, dep_pkgs); } else if (o.upgrade () || o.patch ()) diff --git a/bdep/test.cli b/bdep/test.cli index 18c9b04..a283b1b 100644 --- a/bdep/test.cli +++ b/bdep/test.cli @@ -41,9 +41,9 @@ namespace bdep If no project or package directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is assumed. See \l{bdep-projects-configs(1)} for details on - specifying projects and configurations. Optional \c{\i{cfg-var}...} are - the additional configuration variables to pass to the build system. + configurations are assumed. See \l{bdep-projects-configs(1)} for details + on specifying projects and configurations. Optional \c{\i{cfg-var}...} + are the additional configuration variables to pass to the build system. " } diff --git a/bdep/update.cli b/bdep/update.cli index 4a66298..fc3df84 100644 --- a/bdep/update.cli +++ b/bdep/update.cli @@ -41,9 +41,9 @@ namespace bdep If no project or package directory is specified, then the current working directory is assumed. If no configuration is specified, then the default - configuration is assumed. See \l{bdep-projects-configs(1)} for details on - specifying projects and configurations. Optional \c{\i{cfg-var}...} are - the additional configuration variables to pass to the build system. + configurations are assumed. See \l{bdep-projects-configs(1)} for details + on specifying projects and configurations. Optional \c{\i{cfg-var}...} + are the additional configuration variables to pass to the build system. " } diff --git a/tests/ci.testscript b/tests/ci.testscript index d0407b5..bc3174f 100644 --- a/tests/ci.testscript +++ b/tests/ci.testscript @@ -102,7 +102,11 @@ windows = ($cxx.target.class == 'windows') $init -C @cfg1 &prj-cfg1/***; $init -C @cfg2 &prj-cfg2/***; - $* --all 2>'error: multiple configurations specified for ci' != 0 + $* --all 2>>EOE != 0 + error: package prj is initialized in multiple specified configurations + info: @cfg1 + info: @cfg2 + EOE } : no-commits @@ -393,6 +397,26 @@ windows = ($cxx.target.class == 'windows') EOE } + : diff-configs + : + { + $clone_prj; + $init -C @cfg1 -d prj/libprj &prj-cfg1/***; + + # While at it, test that we fail for uninitialized package. + # + $* -d prj/prj 2>>EOE != 0; + error: package prj is not initialized in any configuration + EOE + + $init -C @cfg2 --config-type host -d prj/prj &prj-cfg2/***; + + $* 2>>~%EOE% + %CI request is queued.*% + %reference: .+% + EOE + } + : single : { diff --git a/tests/config.testscript b/tests/config.testscript index db02947..cb8ed7e 100644 --- a/tests/config.testscript +++ b/tests/config.testscript @@ -22,10 +22,10 @@ deinit += -d prj $clone_root_prj; $* create @cfg cfg-dir $config_cxx 2>>/"EOE" &cfg-dir/***; - created configuration @cfg $~/cfg-dir/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/cfg-dir/ 1 target default,forwarded,auto-synchronized EOE - $status 2>'error: package prj is not initialized in configuration @cfg' != 0; + $status 2>'error: package prj is not initialized in any default configuration(s)' != 0; $init @cfg 2>>/~"%EOE%"; initializing in project $~/prj/ @@ -36,7 +36,7 @@ deinit += -d prj $status >'prj configured 0.1.0-a.0.19700101000000'; $* list @cfg >>/"EOO"; - @cfg $~/cfg-dir/ 1 default,forwarded,auto-synchronized + @cfg $~/cfg-dir/ 1 target default,forwarded,auto-synchronized EOO $update @cfg 2>>~%EOE%; @@ -56,7 +56,7 @@ deinit += -d prj $clone_root_prj; $* create -- @cfg $config_cxx 2>>/"EOE" &prj-cfg/***; - created configuration @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized EOE $init @cfg 2>>/~"%EOE%"; @@ -68,7 +68,7 @@ deinit += -d prj $status >'prj configured 0.1.0-a.0.19700101000000'; $* list >>/"EOO"; - @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized EOO $update @cfg 2>>~%EOE%; @@ -88,7 +88,7 @@ deinit += -d prj $clone_root_prj; $* create cfg $config_cxx 2>>/"EOE" &cfg/***; - created configuration $~/cfg/ 1 default,forwarded,auto-synchronized + created configuration $~/cfg/ 1 target default,forwarded,auto-synchronized EOE $init -c cfg 2>>/~"%EOE%"; @@ -100,7 +100,7 @@ deinit += -d prj $status >'prj configured 0.1.0-a.0.19700101000000'; $* list >>/"EOO"; - $~/cfg/ 1 default,forwarded,auto-synchronized + $~/cfg/ 1 target default,forwarded,auto-synchronized EOO $update -c cfg 2>>~%EOE%; @@ -114,6 +114,38 @@ deinit += -d prj EOE } + : type + : + { + $clone_root_prj; + + $* create --config-type host -- @cfg $config_cxx 2>>/"EOE" &prj-cfg/***; + created configuration @cfg $~/prj-cfg/ 1 host default,forwarded,auto-synchronized + EOE + + $init @cfg 2>>/~"%EOE%"; + initializing in project $~/prj/ + synchronizing: + % new prj.+19700101000000% + EOE + + $status >'prj configured 0.1.0-a.0.19700101000000'; + + $* list @cfg >>/"EOO"; + @cfg $~/prj-cfg/ 1 host default,forwarded,auto-synchronized + EOO + + $update @cfg 2>>~%EOE%; + %(mkdir|c\+\+|ld) .+%{3} + EOE + + $deinit @cfg 2>>/"EOE" + deinitializing in project $~/prj/ + synchronizing: + drop prj + EOE + } + : wipe : { @@ -128,7 +160,7 @@ deinit += -d prj EOE $* create --wipe cfg $config_cxx 2>>/"EOE" &cfg/*** - created configuration $~/cfg/ 1 default,forwarded,auto-synchronized + created configuration $~/cfg/ 1 target default,forwarded,auto-synchronized EOE } } @@ -144,11 +176,11 @@ deinit += -d prj $new -C prj-cfg2 tmp2 $config_cxx 2>! &prj-cfg2/*** &tmp2/***; $* add @cfg1 prj-cfg1 2>>/"EOE"; - added configuration @cfg1 $~/prj-cfg1/ 1 default,forwarded,auto-synchronized + added configuration @cfg1 $~/prj-cfg1/ 1 target default,forwarded,auto-synchronized EOE $* add @cfg2 prj-cfg2 2>>/"EOE"; - added configuration @cfg2 $~/prj-cfg2/ 2 auto-synchronized + added configuration @cfg2 $~/prj-cfg2/ 2 target auto-synchronized EOE $init --all 2>>/~"%EOE%"; @@ -171,8 +203,8 @@ deinit += -d prj EOO $* list >>/"EOO"; - @cfg1 $~/prj-cfg1/ 1 default,forwarded,auto-synchronized - @cfg2 $~/prj-cfg2/ 2 auto-synchronized + @cfg1 $~/prj-cfg1/ 1 target default,forwarded,auto-synchronized + @cfg2 $~/prj-cfg2/ 2 target auto-synchronized EOO $update --all 2>>~%EOE%; @@ -201,7 +233,7 @@ deinit += -d prj $clone_root_prj; $* create -- @cfg $config_cxx &prj-cfg/*** 2>>/"EOE"; - created configuration @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized EOE $init @cfg 2>>/~"%EOE%"; @@ -213,7 +245,7 @@ deinit += -d prj mv prj-cfg prj-cfg2; $* move @cfg prj-cfg2 2>>/"EOE"; - moved configuration @cfg $~/prj-cfg/ 1 to $~/prj-cfg2/ + moved configuration @cfg $~/prj-cfg/ 1 target to $~/prj-cfg2/ info: explicit sync command is required for changes to take effect EOE @@ -234,7 +266,7 @@ deinit += -d prj $clone_root_prj; $* create -- @cfg $config_cxx 2>>/"EOE" &prj-cfg/***; - created configuration @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized EOE $init @cfg 2>>/~"%EOE%"; @@ -244,7 +276,7 @@ deinit += -d prj EOE $* rename @cfg cfg2 2>>/"EOE"; - renamed configuration @cfg $~/prj-cfg/ 1 to @cfg2 + renamed configuration @cfg $~/prj-cfg/ 1 target to @cfg2 EOE $update @cfg2 2>>~%EOE%; @@ -283,7 +315,7 @@ deinit += -d prj EOE $* remove @cfg1 2>>/"EOE"; - removed configuration @cfg1 $~/prj-cfg1/ 1 + removed configuration @cfg1 $~/prj-cfg1/ 1 target EOE $status --all >>EOO; @@ -291,7 +323,7 @@ deinit += -d prj EOO $* list >>/"EOO"; - @cfg2 $~/prj-cfg2/ 2 auto-synchronized + @cfg2 $~/prj-cfg2/ 2 target auto-synchronized EOO $update 2>>/"EOE" != 0; @@ -300,12 +332,12 @@ deinit += -d prj EOE $* set @cfg2 --default --forward --no-auto-sync 2>>/"EOE"; - updated configuration @cfg2 $~/prj-cfg2/ 2 default,forwarded + updated configuration @cfg2 $~/prj-cfg2/ 2 target default,forwarded info: explicit sync command is required for changes to take effect EOE $* list >>/"EOO"; - @cfg2 $~/prj-cfg2/ 2 default,forwarded + @cfg2 $~/prj-cfg2/ 2 target default,forwarded EOO $update 2>>~%EOE%; @@ -318,3 +350,16 @@ deinit += -d prj drop prj EOE } + +: link +: +{ + $clone_root_prj; + + $* create -- @cfg1 $config_cxx 2>! &prj-cfg1/***; + $* create -- @cfg2 $config_cxx 2>! &prj-cfg2/***; + + $* link @cfg1 @cfg2 2>>EOE + linked configuration @cfg1 (target) with configuration @cfg2 (target) + EOE +} diff --git a/tests/init.testscript b/tests/init.testscript index ba67b56..5192145 100644 --- a/tests/init.testscript +++ b/tests/init.testscript @@ -27,7 +27,7 @@ deinit += -d prj $* -C @cfg $config_cxx 'config.cc.poptions=-DTEST' -- '?sys:libprj/*' 2>>/~"%EOE%" &prj-cfg/***; initializing in project $~/prj/ - created configuration @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized synchronizing: % configure sys:libprj.*% % new prj.+19700101000000% @@ -67,7 +67,7 @@ deinit += -d prj $* -C @cfg -- $config_cxx 'config.cc.poptions=-DTEST' -- '?sys:libprj/*' 2>>/~"%EOE%" &prj-cfg/***; initializing in project $~/prj/ - created configuration @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized synchronizing: % configure sys:libprj.*% % new prj.+19700101000000% @@ -99,7 +99,7 @@ deinit += -d prj $* -C @cfg -- -- '?sys:libprj/*' 2>>/~"%EOE%" &prj-cfg/***; initializing in project $~/prj/ - created configuration @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized synchronizing: % configure sys:libprj.*% % new prj.+19700101000000% @@ -125,20 +125,20 @@ deinit += -d prj # Pre-create configurations. # - $new -C prj-cfg1 tmp $config_cxx 2>! &prj-cfg1/*** &tmp/***; - $init -C prj-cfg2 -d tmp $config_cxx 2>! &prj-cfg2/***; + $new -C prj-cfg1 --no-default tmp $config_cxx 2>! &prj-cfg1/*** &tmp/***; + $init -C prj-cfg2 --config-type host -d tmp $config_cxx 2>! &prj-cfg2/***; $* -A @cfg1 '?sys:libprj/*' 2>>/~"%EOE%"; initializing in project $~/prj/ - added configuration @cfg1 $~/prj-cfg1/ 1 default,forwarded,auto-synchronized + added configuration @cfg1 $~/prj-cfg1/ 1 target default,forwarded,auto-synchronized synchronizing: % configure sys:libprj.*% % new prj.+19700101000000% EOE - $* -A prj-cfg2 @cfg2 '?sys:libprj/*' 2>>/~"%EOE%"; + $* -A prj-cfg2 --no-default @cfg2 '?sys:libprj/*' 2>>/~"%EOE%"; initializing in project $~/prj/ - added configuration @cfg2 $~/prj-cfg2/ 2 auto-synchronized + added configuration @cfg2 $~/prj-cfg2/ 2 host auto-synchronized synchronizing: % configure sys:libprj.*% % new prj.+19700101000000% @@ -188,7 +188,7 @@ deinit += -d prj $* -C @cfg $config_cxx 2>>/~"%EOE%" &prj-cfg/***; initializing in project $~/prj/ - created configuration @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized synchronizing: % new prj.+19700101000000% EOE diff --git a/tests/new.testscript b/tests/new.testscript index 4d98feb..727f338 100644 --- a/tests/new.testscript +++ b/tests/new.testscript @@ -1836,7 +1836,7 @@ subdir=hello,no-subdir-source \ { $* -C prj-config @cfg prj cc $config_cxx 2>>/~"%EOE%" &prj/*** &prj-config/***; created new executable project prj in $~/prj/ - created configuration @cfg $~/prj-config/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-config/ 1 target default,forwarded,auto-synchronized synchronizing: % new prj.+19700101000000% EOE @@ -1858,7 +1858,7 @@ subdir=hello,no-subdir-source \ { $* -C -@cfg prj cc $config_cxx 2>>/~"%EOE%" &prj/*** &prj-cfg/***; created new executable project prj in $~/prj/ - created configuration @cfg $~/prj-cfg/ 1 default,forwarded,auto-synchronized + created configuration @cfg $~/prj-cfg/ 1 target default,forwarded,auto-synchronized synchronizing: % new prj.+19700101000000% EOE diff --git a/tests/publish.testscript b/tests/publish.testscript index 8f5ec85..07c3632 100644 --- a/tests/publish.testscript +++ b/tests/publish.testscript @@ -33,7 +33,7 @@ g = git -C prj >! 2>! # duplicate submissions. We will use unique version for each test, # incrementing the patch version for 1.0.X. # -# Next version to use: 1.0.20 +# Next version to use: 1.0.22 # # Normally we disable the progress indication that complicates stderr output @@ -104,7 +104,11 @@ g = git -C prj >! 2>! $init -C @cfg1 &prj-cfg1/***; $init -C @cfg2 &prj-cfg2/***; - $* --all 2>'error: multiple configurations specified for publish' != 0 + $* --all 2>>EOE != 0 + error: package prj is initialized in multiple specified configurations + info: @cfg1 + info: @cfg2 + EOE } : snapshot @@ -152,6 +156,42 @@ g = git -C prj >! 2>! EOE } + : diff-configs + : + { + $clone_prj; + sed -i -e 's/^(version:) .*$/\1 1.0.20/' prj/libprj/manifest; + sed -i -e 's/^(version:) .*$/\1 1.0.20/' prj/prj/manifest; + + $init -C @cfg1 -d prj/libprj &prj-cfg1/***; + $init -C @cfg2 --config-type host -d prj/prj &prj-cfg2/***; + + $* 2>>~%EOE% + %package submission is queued(: \.*libprj/1.0.20)?%d + %reference: .{12}% + %package submission is queued(: \.*prj/1.0.20)?%d + %reference: .{12}% + EOE + } + + : diff-configs-forward + : + { + $clone_prj; + sed -i -e 's/^(version:) .*$/\1 1.0.21/' prj/libprj/manifest; + sed -i -e 's/^(version:) .*$/\1 1.0.21/' prj/prj/manifest; + + $init -C @cfg1 --no-default --forward -d prj/libprj &prj-cfg1/***; + $init -C @cfg2 --config-type host --no-default --forward -d prj/prj &prj-cfg2/***; + + $* --forward 2>>~%EOE% + %package submission is queued(: \.*libprj/1.0.21)?%d + %reference: .{12}% + %package submission is queued(: \.*prj/1.0.21)?%d + %reference: .{12}% + EOE + } + : single : { diff --git a/tests/update.testscript b/tests/update.testscript index 7e03238..3823b9d 100644 --- a/tests/update.testscript +++ b/tests/update.testscript @@ -88,3 +88,46 @@ deinit += -d prj drop pkg2 EOE } + +: multi-default-cfg +: +{ + $new -t empty prj &prj/***; + + $new --package pkg1 -d prj; + $new --package pkg2 -d prj; + + $init -C @cfg1 -d prj/pkg1 &prj-cfg1/***; + $init -C @cfg2 -d prj/pkg2 --config-type host &prj-cfg2/***; + + # Update. + # + $* -d prj 2>>~%EOE%; + in configuration @cfg1: + %(mkdir|c\+\+|ld) .+pkg1.+%{3} + + in configuration @cfg2: + %(mkdir|c\+\+|ld) .+pkg2.+%{3} + EOE + + # Clean. + # + $clean -d prj 2>>~%EOE%; + in configuration @cfg1: + %rm .+pkg1.+%{3} + + in configuration @cfg2: + %rm .+pkg2.+%{3} + EOE + + $deinit 2>>/"EOE" + deinitializing in project $~/prj/ + in configuration @cfg1: + synchronizing: + drop pkg1 + + in configuration @cfg2: + synchronizing: + drop pkg2 + EOE +} -- cgit v1.1