aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-07-11 21:16:08 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-08-12 12:53:05 +0300
commit1ac2b1310851b3da03318797231f3832f4804c79 (patch)
treeae06ffe5409b8230f930499463b694eb96c11f1f
parent6ecb087d239a19ccbc272a5f2333a16d4cfe9f59 (diff)
Allow using package names in commands (GH issue #355)
-rw-r--r--bdep/build.txx14
-rw-r--r--bdep/ci.cli4
-rw-r--r--bdep/ci.cxx18
-rw-r--r--bdep/clean.cli4
-rw-r--r--bdep/deinit.cli4
-rw-r--r--bdep/deinit.cxx11
-rw-r--r--bdep/init.cli11
-rw-r--r--bdep/project.cxx75
-rw-r--r--bdep/project.hxx34
-rw-r--r--bdep/projects-configs.cli11
-rw-r--r--bdep/publish.cli4
-rw-r--r--bdep/publish.cxx16
-rw-r--r--bdep/status.cli9
-rw-r--r--bdep/status.cxx142
-rw-r--r--bdep/sync.cli10
-rw-r--r--bdep/sync.cxx198
-rw-r--r--bdep/test.cli4
-rw-r--r--bdep/update.cli4
-rw-r--r--bdep/utility.hxx2
-rw-r--r--tests/ci.testscript41
-rw-r--r--tests/publish.testscript60
-rw-r--r--tests/status.testscript179
-rw-r--r--tests/sync.testscript61
-rw-r--r--tests/update.testscript42
24 files changed, 852 insertions, 106 deletions
diff --git a/bdep/build.txx b/bdep/build.txx
index a9b237c..3c5fd7c 100644
--- a/bdep/build.txx
+++ b/bdep/build.txx
@@ -22,17 +22,18 @@ namespace bdep
{
tracer trace ("build");
- // Save cfg-vars with some sanity checking.
+ // Save cfg-vars and the package names.
//
strings cfg_vars;
+ strings ps;
while (args.more ())
{
const char* a (args.next ());
- if (strchr (a , '=') == nullptr)
- fail << "'" << a << "' does not look like a variable assignment";
-
- cfg_vars.push_back (trim (a));
+ if (strchr (a , '=') != nullptr)
+ cfg_vars.push_back (trim (a));
+ else
+ ps.emplace_back (a);
}
// The same ignore/load story as in sync.
@@ -44,6 +45,9 @@ namespace bdep
const dir_path& prj (pp.project);
+ if (!ps.empty ())
+ pp.append (find_project_packages (prj, ps).first.packages);
+
// Load the configurations without keeping the database open longer than
// necessary.
//
diff --git a/bdep/ci.cli b/bdep/ci.cli
index 99b7834..7707935 100644
--- a/bdep/ci.cli
+++ b/bdep/ci.cli
@@ -13,7 +13,7 @@ namespace bdep
{
"<options>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>",
"\h|SYNOPSIS|
@@ -21,7 +21,7 @@ namespace bdep
\c{\b{bdep ci} [<options>] [<cfg-spec>] [<pkg-spec>]}
\c{<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a} | \b{--forward}\n
- <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>}
\h|DESCRIPTION|
diff --git a/bdep/ci.cxx b/bdep/ci.cxx
index 0b26b35..fad089a 100644
--- a/bdep/ci.cxx
+++ b/bdep/ci.cxx
@@ -176,7 +176,7 @@ namespace bdep
}
int
- cmd_ci (const cmd_ci_options& o, cli::scanner&)
+ cmd_ci (const cmd_ci_options& o, cli::scanner& args)
{
tracer trace ("ci");
@@ -189,6 +189,12 @@ namespace bdep
fail << n << " specified together with --forward";
}
+ // Save the package names.
+ //
+ strings ns;
+ while (args.more ())
+ ns.emplace_back (args.next ());
+
// Collect the packages manifest value overrides parsing the --override,
// etc options and verify that the resulting overrides list contains valid
// package manifest values and is semantically correct.
@@ -409,18 +415,22 @@ namespace bdep
//
// In the forward mode we use package's forwarded source directories to
// obtain their versions. Also we load the project packages if the
- // specified directory is a project directory.
+ // specified directory is a project directory, unless some package names
+ // are specified on the command line.
//
// Note also that no pre-sync is needed since we are only getting versions
// (via the info meta-operation).
//
project_packages pp (
find_project_packages (o,
- false /* ignore_packages */,
- o.forward () /* load_packages */));
+ false /* ignore_packages */,
+ o.forward () && ns.empty () /* load_packages */));
const dir_path& prj (pp.project);
+ if (!ns.empty ())
+ pp.append (find_project_packages (prj, ns).first.packages);
+
// Collect package names, versions, and configurations used (except for
// the forward mode).
//
diff --git a/bdep/clean.cli b/bdep/clean.cli
index 6a2e140..759bc15 100644
--- a/bdep/clean.cli
+++ b/bdep/clean.cli
@@ -12,7 +12,7 @@ namespace bdep
{
"<options>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>
<cfg-var>",
@@ -21,7 +21,7 @@ namespace bdep
\c{\b{bdep clean} [<options>] [<pkg-spec>] [<cfg-spec>] [<cfg-var>...]}
\c{<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a}\n
- <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>}
\h|DESCRIPTION|
diff --git a/bdep/deinit.cli b/bdep/deinit.cli
index 3ec0e72..f0e2320 100644
--- a/bdep/deinit.cli
+++ b/bdep/deinit.cli
@@ -12,7 +12,7 @@ namespace bdep
{
"<options>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>",
"\h|SYNOPSIS|
@@ -20,7 +20,7 @@ namespace bdep
\c{\b{bdep deinit} [<options>] [<pkg-spec>] [<cfg-spec>]}
\c{<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a}\n
- <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>}
\h|DESCRIPTION|
diff --git a/bdep/deinit.cxx b/bdep/deinit.cxx
index d36f39c..19424b4 100644
--- a/bdep/deinit.cxx
+++ b/bdep/deinit.cxx
@@ -128,10 +128,16 @@ namespace bdep
}
int
- cmd_deinit (const cmd_deinit_options& o, cli::scanner&)
+ cmd_deinit (const cmd_deinit_options& o, cli::scanner& args)
{
tracer trace ("deinit");
+ // Save the package names.
+ //
+ strings ns;
+ while (args.more ())
+ ns.emplace_back (args.next ());
+
bool force (o.force ());
// The same ignore/load story as in sync.
@@ -143,6 +149,9 @@ namespace bdep
const dir_path& prj (pp.project);
+ if (!ns.empty ())
+ pp.append (find_project_packages (prj, ns).first.packages);
+
if (verb)
text << "deinitializing in project " << prj;
diff --git a/bdep/init.cli b/bdep/init.cli
index f1b78f4..82b8c4d 100644
--- a/bdep/init.cli
+++ b/bdep/init.cli
@@ -43,10 +43,13 @@ 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 (failing if multiple default configurations are
- present). See \l{bdep-projects-configs(1)} for details on specifying
- projects and configurations. Optional <pkg-args> are the additional
- dependency packages and/or configuration variables to pass to the
- underlying \l{bpkg-pkg-build(1)} command.
+ present). Note that unlike in all other commands, in \cb{init} the
+ package to initialize in <pkg-spec> cannot be specified as a <pkg> name,
+ only as a directory with \c{\b{--directory}|\b{-d}}. See
+ \l{bdep-projects-configs(1)} for details on specifying projects and
+ configurations. Optional <pkg-args> 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/project.cxx b/bdep/project.cxx
index 7321cc4..e55ccb0 100644
--- a/bdep/project.cxx
+++ b/bdep/project.cxx
@@ -447,6 +447,63 @@ namespace bdep
return r;
}
+ pair<project_packages, strings>
+ find_project_packages (dir_path prj,
+ const strings& pkgs,
+ bool ignore_nf,
+ bool allow_empty)
+ {
+ package_locations pls (load_packages (prj, allow_empty));
+
+ package_locations rpls;
+ strings rps;
+
+ for (const string& p: pkgs)
+ {
+ // Skip duplicates to be consistent with the other function overloads.
+ //
+ if (find_if (rpls.begin (), rpls.end (),
+ [&p] (const package_location& pl)
+ {
+ return pl.name == p;
+ }) != rpls.end ())
+ continue;
+
+ auto i (find_if (pls.begin (), pls.end (),
+ [&p] (const package_location& pl)
+ {
+ return pl.name == p;
+ }));
+
+ if (i != pls.end ())
+ {
+ rpls.push_back (move (*i));
+
+ // Keep name in the definite state, since it is referred during search.
+ //
+ i->name = package_name ();
+ }
+ else if (ignore_nf)
+ {
+ rps.push_back (p);
+ }
+ else
+ {
+ try
+ {
+ package_name n (p);
+ fail << "no package " << n << " in project " << prj;
+ }
+ catch (const invalid_argument& e)
+ {
+ fail << "package name '" << p << "' is invalid: " << e;
+ }
+ }
+ }
+
+ return make_pair (project_packages {move (prj), move (rpls)}, move (rps));
+ }
+
void
verify_project_packages (const project_packages& pp,
const pair<configurations, bool>& cfgs)
@@ -553,4 +610,22 @@ namespace bdep
fail << "package " << p << " does not use standard version" <<
info << "perhaps the package does not load the version module?";
}
+
+ // project_packages
+ //
+ void project_packages::
+ append (package_locations&& pls)
+ {
+ for (package_location& l: pls)
+ {
+ if (find_if (packages.begin (), packages.end (),
+ [&l] (const package_location& pl)
+ {
+ return pl.path == l.path;
+ }) == packages.end ())
+ {
+ packages.push_back (move (l));
+ }
+ }
+ }
}
diff --git a/bdep/project.hxx b/bdep/project.hxx
index c40fe61..e9ec3c4 100644
--- a/bdep/project.hxx
+++ b/bdep/project.hxx
@@ -241,6 +241,11 @@ namespace bdep
{
dir_path project;
package_locations packages;
+
+ // Append package locations suppressing duplicates.
+ //
+ void
+ append (package_locations&&);
};
// Search project packages in the specified directories or the current
@@ -274,6 +279,35 @@ namespace bdep
return find_project_packages (o, true /* ignore_packages */).project;
}
+ // Search for the specified package names in the specified project
+ // directory. Fail if some packages are not found in the project, unless
+ // ignore_not_found is true in which case return them in the second half of
+ // the pair.
+ //
+ pair<project_packages, strings>
+ find_project_packages (dir_path,
+ const strings&,
+ bool ignore_not_found = false,
+ bool allow_empty = false);
+
+ inline pair<project_packages, strings>
+ find_project_packages (const dir_paths& dirs,
+ const strings& pkgs,
+ bool ignore_nf = false,
+ bool ae = false)
+ {
+ return find_project_packages (find_project (dirs), pkgs, ignore_nf, ae);
+ }
+
+ inline pair<project_packages, strings>
+ find_project_packages (const project_options& po,
+ const strings& pkgs,
+ bool ignore_nf = false,
+ bool ae = false)
+ {
+ return find_project_packages (po.directory (), pkgs, ignore_nf, ae);
+ }
+
// Verify that each package is present in at least one configuration.
//
// Note that the default configurations fallback indication (see above) is
diff --git a/bdep/projects-configs.cli b/bdep/projects-configs.cli
index a5c5808..844099b 100644
--- a/bdep/projects-configs.cli
+++ b/bdep/projects-configs.cli
@@ -10,7 +10,7 @@ include <bdep/common-options.hxx>;
{
"<command>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>",
"
@@ -19,7 +19,7 @@ include <bdep/common-options.hxx>;
\c{\b{bdep} <command> [<pkg-spec>] [<cfg-spec>] ...}
\c{<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a}\n
- <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>}
\h|DESCRIPTION|
@@ -39,9 +39,10 @@ include <bdep/common-options.hxx>;
\cb{git(1)}.
Alternatively, the project or (several) package directories can be specified
- with the \c{\b{--directory}|\b{-d}} options. Note that \cb{bdep} operates
- on a single project but potentially multiple packages belonging to said
- project at a time.
+ with the \c{\b{--directory}|\b{-d}} options. Additionally, in all the
+ commands except \cb{init}, an initialized project package can be specified
+ as a <pkg> name. Note that \cb{bdep} operates on a single project but
+ potentially multiple packages belonging to said project at a time.
Some \cb{bdep} commands, such as \cb{fetch}, operate on the whole project.
If such a command is given a package directory (either as the working
diff --git a/bdep/publish.cli b/bdep/publish.cli
index 85925ec..89a8e4d 100644
--- a/bdep/publish.cli
+++ b/bdep/publish.cli
@@ -14,14 +14,14 @@ namespace bdep
{
"<options>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>",
"\h|SYNOPSIS|
\c{\b{bdep publish} [<options>] [<cfg-spec>] [<pkg-spec>]}
- \c{<pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ \c{<pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>\n
<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a} | \b{--forward}}
diff --git a/bdep/publish.cxx b/bdep/publish.cxx
index 3bdbf46..9a5f417 100644
--- a/bdep/publish.cxx
+++ b/bdep/publish.cxx
@@ -862,7 +862,7 @@ namespace bdep
}
int
- cmd_publish (const cmd_publish_options& o, cli::scanner&)
+ cmd_publish (const cmd_publish_options& o, cli::scanner& args)
{
tracer trace ("publish");
@@ -875,6 +875,12 @@ namespace bdep
fail << n << " specified together with --forward";
}
+ // Save the package names.
+ //
+ strings ns;
+ while (args.more ())
+ ns.emplace_back (args.next ());
+
// If we are publishing the entire project, then we have two choices: we
// can publish all the packages in the project or we can only do so for
// packages that were initialized in the configuration that we are going
@@ -885,10 +891,14 @@ namespace bdep
//
project_packages pp (
find_project_packages (o,
- false /* ignore_packages */,
- true /* load_packages */));
+ false /* ignore_packages */,
+ ns.empty () /* load_packages */));
const dir_path& prj (pp.project);
+
+ if (!ns.empty ())
+ pp.append (find_project_packages (prj, ns).first.packages);
+
package_locations& pkgs (pp.packages);
// If we are submitting multiple packages, verify they are from the same
diff --git a/bdep/status.cli b/bdep/status.cli
index 1360606..f1d9747 100644
--- a/bdep/status.cli
+++ b/bdep/status.cli
@@ -12,7 +12,7 @@ namespace bdep
{
"<options>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>
<dep-spec> <pkg> <ver>",
@@ -22,7 +22,7 @@ namespace bdep
\c{<dep-spec> = <pkg>[\b{/}<ver>]\n
<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a}\n
- <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>}
\h|DESCRIPTION|
@@ -35,6 +35,11 @@ namespace bdep
configurations are assumed. See \l{bdep-projects-configs(1)} for details
on specifying projects and configurations.
+ Note that if a package that is specified as a <pkg> name (as opposed to
+ as a directory with \c{\b{--directory}|\b{-d}}) belongs to the project
+ and is initialized in any of the specified configurations, then it is
+ interpreted as <pkg-spec>. Otherwise, it is interpreted as <dep-spec>.
+
If no <dep-spec> arguments are specified, then \cb{status} prints the
status of the project's packages. Otherwise, the status of the specified
dependency packages is printed. Additionally, the status of immediate or
diff --git a/bdep/status.cxx b/bdep/status.cxx
index c8b74b4..22bcb54 100644
--- a/bdep/status.cxx
+++ b/bdep/status.cxx
@@ -348,13 +348,117 @@ namespace bdep
if (o.immediate () && o.recursive ())
fail << "both --immediate|-i and --recursive|-r specified";
+ bool json (o.stdout_format () == stdout_format::json);
+
+ optional<pair<configurations, bool>> cs; // Load if/when required.
+
+ auto load_configurations = [&o, json, &trace] (const dir_path& prj)
+ {
+ database db (open (prj, trace));
+
+ transaction t (db.begin ());
+
+ // If --all|-a is specified and the project has no associated
+ // configurations let's print an empty array of configurations for the
+ // JSON format instead of failing (as we do for the lines format).
+ //
+ optional<pair<configurations, bool>> r (
+ find_configurations (o,
+ prj,
+ t,
+ true /* fallback_default */,
+ true /* validate */,
+ json /* allow_none */));
+
+ t.commit ();
+
+ return r;
+ };
+
// We have two pretty different modes: project package status and
- // dependency package status (have arguments).
+ // dependency package status (<dep-specs> are specified).
+ //
+ // Sort arguments (if any) into dep-specs and pkg-specs as follows:
+ //
+ // - If the argument contains '/' (package version) or this package
+ // doesn't belong to this project or is not initialized, then we assume
+ // dep-spec.
+ //
+ // - Otherwise, we assume pkg-spec.
//
strings dep_pkgs;
- for (; args.more (); dep_pkgs.push_back (args.next ())) ;
+ package_locations pkgs;
+ {
+ strings ns;
+ while (args.more ())
+ {
+ const char* a (args.next ());
- bool json (o.stdout_format () == stdout_format::json);
+ if (strchr (a, '/') != nullptr)
+ dep_pkgs.push_back (a);
+ else
+ ns.push_back (a);
+ }
+
+ // Add packages which don't belong to the project or are not initialized
+ // to dep_pkgs and to pkgs otherwise.
+ //
+ if (!ns.empty ())
+ {
+ pair<project_packages, strings> pps (
+ find_project_packages (o,
+ ns,
+ true /* ignore_not_found */,
+ json /* allow_empty */));
+
+ // Assume the packages that don't belong to the project as dep-specs.
+ //
+ dep_pkgs.insert (dep_pkgs.end (),
+ make_move_iterator (pps.second.begin ()),
+ make_move_iterator (pps.second.end ()));
+
+ // Assume the initialized packages of the project as the pkg-specs and
+ // the dep-specs otherwise.
+ //
+ // Note that here we check if the packages are initialized only in the
+ // specified configurations.
+ //
+ project_packages& pp (pps.first);
+
+ if (!pp.packages.empty ())
+ {
+ cs = load_configurations (pp.project);
+
+ for (package_location& p: pp.packages)
+ {
+ bool init (false);
+
+ for (const shared_ptr<configuration>& c: cs->first)
+ {
+ if (find_if (c->packages.begin (),
+ c->packages.end (),
+ [&p] (const package_state& s)
+ {
+ return p.name == s.name;
+ }) != c->packages.end ())
+ {
+ init = true;
+ break;
+ }
+ }
+
+ if (init)
+ pkgs.push_back (move (p));
+ else
+ dep_pkgs.push_back (move (p.name).string ());
+ }
+
+ if (!pkgs.empty () && !dep_pkgs.empty ())
+ fail << "initialized package " << pkgs[0].name
+ << " specified with dependency package " << dep_pkgs[0];
+ }
+ }
+ }
// The same ignore/load story as in sync for the lines format.
//
@@ -365,40 +469,28 @@ namespace bdep
//
project_packages pp (
find_project_packages (o,
- !dep_pkgs.empty () /* ignore_packages */,
- json /* load_packages */,
- json /* allow_empty */));
+ !dep_pkgs.empty () /* ignore_packages */,
+ json && pkgs.empty () /* load_packages */,
+ json /* allow_empty */));
- const dir_path& prj (pp.project);
-
- database db (open (prj, trace));
+ if (!pkgs.empty ())
+ pp.append (move (pkgs));
configurations cfgs;
{
- transaction t (db.begin ());
-
- // If --all|-a is specified and the project has no associated
- // configurations let's print an empty array of configurations for the
- // JSON format instead of failing (as we do for the lines format).
+ // Load project configurations, if not loaded yet.
//
- pair<configurations, bool> cs (
- find_configurations (o,
- prj,
- t,
- true /* fallback_default */,
- true /* validate */,
- json /* allow_none */));
-
- t.commit ();
+ if (!cs)
+ cs = load_configurations (pp.project);
// If specified, verify packages are present in at least one
// configuration. But not for the JSON format where we also print
// statuses of uninitialized packages.
//
if (!pp.packages.empty () && !json)
- verify_project_packages (pp, cs);
+ verify_project_packages (pp, *cs);
- cfgs = move (cs.first);
+ cfgs = move (cs->first);
}
switch (o.stdout_format ())
diff --git a/bdep/sync.cli b/bdep/sync.cli
index 6e3082f..bd7f548 100644
--- a/bdep/sync.cli
+++ b/bdep/sync.cli
@@ -12,7 +12,7 @@ namespace bdep
{
"<options>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>
<dep-spec> <pkg> <ver>
<pkg-args> <pkg> <cfg-var>",
@@ -27,7 +27,7 @@ namespace bdep
\c{<dep-spec> = <pkg>[\b{/}<ver>]\n
<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a}\n
- <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>\n
<pkg-args> = (\b{?}<pkg> | <cfg-var>)...}
@@ -64,6 +64,12 @@ namespace bdep
respectively. Alternative to \cb{--upgrade} and \cb{--patch}, the desired
upgrade (or downgrade) version can be specified explicitly.
+ Note that if a package that is specified as a <pkg> name (as opposed to
+ as a directory with \c{\b{--directory}|\b{-d}}) belongs to the project
+ and is initialized in any of the specified configurations, then it is
+ interpreted as <pkg-spec> of the first or second form. Otherwise, it is
+ interpreted as <dep-spec> of the third from of the command line.
+
Note also that \c{\b{--immediate}|\b{-i}} or \c{\b{--recursive}|\b{-r}}
can only be specified with an explicit \cb{--upgrade} or \cb{--patch}.
diff --git a/bdep/sync.cxx b/bdep/sync.cxx
index d92d86a..e9fd5be 100644
--- a/bdep/sync.cxx
+++ b/bdep/sync.cxx
@@ -2488,22 +2488,6 @@ namespace bdep
fail << n << " requires explicit --upgrade|-u or --patch|-p";
}
- // Sort arguments (if any) into pkg-args and dep-spec: if the argument
- // starts with '?' (dependency flag) or contains '=' (config variable),
- // then we assume it is pkg-args.
- //
- // Note: scan_argument() passes through groups.
- //
- strings pkg_args;
- strings dep_pkgs;
- while (args.more ())
- {
- const char* r (args.peek ());
- scan_argument (
- (*r == '?' || strchr (r, '=') != nullptr) ? pkg_args : dep_pkgs,
- args);
- }
-
// --hook
//
if (o.hook_specified ())
@@ -2518,6 +2502,160 @@ namespace bdep
o.implicit (true); // Implies --implicit.
}
+ optional<pair<configurations, bool>> cs; // Load if/when required.
+
+ auto load_configurations = [&o, &trace] (const dir_path& prj)
+ {
+ database db (open (prj, trace));
+
+ transaction t (db.begin ());
+ optional<pair<configurations, bool>> r (find_configurations (o, prj, t));
+ t.commit ();
+
+ return r;
+ };
+
+ // Note: in the implicit mode we don't search the current working
+ // directory for a project.
+ //
+ bool implicit_no_orig (o.implicit () && !o.directory_specified ());
+
+ // Sort arguments (if any) into pkg-args, dep-specs, and pkg-specs as
+ // follows:
+ //
+ // - If the argument starts with '?' (dependency flag) or contains '='
+ // (config variable), then we assume it is pkg-arg.
+ //
+ // - Otherwise, if this is an implicit sync without an originating project
+ // or the argument contains '/' (package version) or this package
+ // doesn't belong to this project or is not initialized, then we assume
+ // dep-spec.
+ //
+ // - Otherwise, we assume pkg-spec.
+ //
+ // Note: scan_argument() passes through groups.
+ //
+ strings pkg_args;
+ strings dep_pkgs;
+ package_locations pkgs;
+ {
+ // Collect the package names (non pkg-args) into dep_pkgs. Those which
+ // we are not sure about if they are really dep-specs or are pkg-specs
+ // (don't contain version and we are not in the `implicit sync without
+ // an originating project` mode) we also stash together with their group
+ // index ranges (in the [n, k) form) in the parallel ns and gs vectors.
+ //
+ strings ns; // Names.
+ vector<pair<size_t, size_t>> gs; // Group ranges ([n, n) in no group).
+
+ while (args.more ())
+ {
+ const char* r (args.peek ());
+
+ if (*r == '?' || strchr (r, '=') != nullptr)
+ {
+ scan_argument (pkg_args, args);
+ }
+ else
+ {
+ bool ambiguous (!implicit_no_orig && strchr (r, '/') == nullptr);
+
+ size_t n (dep_pkgs.size ());
+ scan_argument (dep_pkgs, args);
+
+ if (ambiguous)
+ {
+ ns.push_back (dep_pkgs[n]);
+ gs.emplace_back (n + 1, dep_pkgs.size ());
+ }
+ }
+ }
+
+ // Move the initialized packages of this project out of dep_pkgs into
+ // pkgs.
+ //
+ // Note that currently we don't support groups for initialized packages,
+ // but may add such support in the future.
+ //
+ if (!ns.empty ())
+ {
+ pair<project_packages, strings> pps (
+ find_project_packages (o, ns, true /* ignore_not_found */));
+
+ // Also assume the initialized packages of the project are pkg-specs
+ // and dep-specs otherwise.
+ //
+ // Note that here we check if the packages are initialized in only the
+ // specified configurations.
+ //
+ project_packages& pp (pps.first);
+
+ if (!pp.packages.empty ())
+ {
+ cs = load_configurations (pp.project);
+
+ // Add initialized packages to pkgs.
+ //
+ for (package_location& p: pp.packages)
+ {
+ for (const shared_ptr<configuration>& c: cs->first)
+ {
+ if (find_if (c->packages.begin (),
+ c->packages.end (),
+ [&p] (const package_state& s)
+ {
+ return p.name == s.name;
+ }) != c->packages.end ())
+ {
+ pkgs.push_back (move (p));
+ break;
+ }
+ }
+ }
+
+ // Remove initialized packages from dep_pkgs.
+ //
+ if (!pkgs.empty ())
+ {
+ // Iterate ns in the reverse order not to invalidate the index
+ // ranges.
+ //
+ for (size_t i (ns.size () - 1); true; --i)
+ {
+ const string& n (ns[i]);
+
+ if (find_if (pkgs.begin (),
+ pkgs.end (),
+ [&n] (const package_location& l)
+ {
+ return l.name == n;
+ }) != pkgs.end ())
+ {
+ // Note: currenlty we don't suport groups for initialized
+ // packages.
+ //
+ if (gs[i].first != gs[i].second)
+ fail << "initialized package " << n << " specified with "
+ << "arguments group";
+
+ // Erase the package name together with its group.
+ //
+ dep_pkgs.erase (dep_pkgs.begin () + gs[i].first - 1,
+ dep_pkgs.begin () + gs[i].second);
+ }
+
+ if (i == 0)
+ break;
+ }
+ }
+
+ if (!pkgs.empty () && !dep_pkgs.empty ())
+ fail << "initialized package " << pkgs[0].name
+ << " specified with dependency package " << dep_pkgs[0];
+ }
+ }
+ }
+
// --implicit
//
if (o.implicit ())
@@ -2557,10 +2695,7 @@ namespace bdep
bool default_fallback (false);
- // In the implicit mode we don't search the current working directory
- // for a project.
- //
- if (o.directory_specified () || !o.implicit ())
+ if (!implicit_no_orig)
{
// We could be running from a package directory (or the user specified
// one with -d) that has not been init'ed in this configuration. This,
@@ -2577,32 +2712,29 @@ namespace bdep
!dep_pkgs.empty () /* ignore_packages */,
false /* load_packages */));
+ if (!pkgs.empty ())
+ pp.append (move (pkgs));
+
// Initialize tmp directory.
//
init_tmp (pp.project);
- // Load project configurations.
+ // Load project configurations, if not loaded yet.
//
- pair<configurations, bool> cs;
- {
- database db (open (pp.project, trace));
-
- transaction t (db.begin ());
- cs = find_configurations (o, pp.project, t);
- t.commit ();
- }
+ if (!cs)
+ cs = load_configurations (pp.project);
// If specified, verify packages are present in at least one
// configuration.
//
if (!pp.packages.empty ())
- verify_project_packages (pp, cs);
+ verify_project_packages (pp, *cs);
prj = move (pp.project);
prj_pkgs = move (pp.packages);
- cfgs.assign (make_move_iterator (cs.first.begin ()),
- make_move_iterator (cs.first.end ()));
- default_fallback = cs.second;
+ cfgs.assign (make_move_iterator (cs->first.begin ()),
+ make_move_iterator (cs->first.end ()));
+ default_fallback = cs->second;
}
else
{
diff --git a/bdep/test.cli b/bdep/test.cli
index a283b1b..35d099d 100644
--- a/bdep/test.cli
+++ b/bdep/test.cli
@@ -12,7 +12,7 @@ namespace bdep
{
"<options>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>
<cfg-var>",
@@ -21,7 +21,7 @@ namespace bdep
\c{\b{bdep test} [<options>] [<pkg-spec>] [<cfg-spec>] [<cfg-var>...]}
\c{<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a}\n
- <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>}
\h|DESCRIPTION|
diff --git a/bdep/update.cli b/bdep/update.cli
index fc3df84..4c3f289 100644
--- a/bdep/update.cli
+++ b/bdep/update.cli
@@ -12,7 +12,7 @@ namespace bdep
{
"<options>
<prj-spec> <prj-dir>
- <pkg-spec> <pkg-dir>
+ <pkg-spec> <pkg> <pkg-dir>
<cfg-spec> <cfg-name> <cfg-dir>
<cfg-var>",
@@ -21,7 +21,7 @@ namespace bdep
\c{\b{bdep update} [<options>] [<pkg-spec>] [<cfg-spec>] [<cfg-var>...]}
\c{<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a}\n
- <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <pkg-spec> = (<pkg> | (\b{--directory}|\b{-d} <pkg-dir>))... | <prj-spec>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>}
\h|DESCRIPTION|
diff --git a/bdep/utility.hxx b/bdep/utility.hxx
index e8678eb..3438fed 100644
--- a/bdep/utility.hxx
+++ b/bdep/utility.hxx
@@ -298,6 +298,8 @@ namespace bdep
// Scan and return/append arguments preserving grouping.
//
+ // Note: the group is always appended after the argument.
+ //
void
scan_argument (strings&, cli::group_scanner&);
diff --git a/tests/ci.testscript b/tests/ci.testscript
index 7f3e538..b051dfd 100644
--- a/tests/ci.testscript
+++ b/tests/ci.testscript
@@ -910,10 +910,26 @@ windows = ($cxx.target.class == 'windows')
$build 'configure:' prj/@prj-cfg/,forward &prj/build/bootstrap/*** 2>!;
- $* --no-progress --forward 2>>~%EOE%
+ $* --no-progress --forward 2>>~%EOE%;
%CI request is queued.*%
%reference: .+%
EOE
+
+ # While at it, test specifying a package name on the command line.
+ #
+ # Suppress the --yes option.
+ #
+ test.arguments = $regex.apply($test.arguments, '^--yes$', '');
+
+ $* --no-progress --forward prj <'y' 2>>~"%EOE%"
+ submitting:
+ to: $server
+ % in: $repository#non-standard-version@.{40}%
+ package: prj
+ version: 12345
+ %continue\\?.+ CI request is queued.*%
+ %reference: .+%
+ EOE
}
}
@@ -954,6 +970,27 @@ windows = ($cxx.target.class == 'windows')
EOE
}
+ : pkg-by-name
+ :
+ {
+ $clone_prj;
+ $init -C @cfg &prj-cfg/***;
+
+ # Suppress the --yes option.
+ #
+ test.arguments = $regex.apply($test.arguments, '^--yes$', '');
+
+ $* libprj <'y' 2>>~"%EOE%"
+ submitting:
+ to: $server
+ % in: $repository#master@.{40}%
+ package: libprj
+ version: 1.0.1
+ %continue\\?.+ CI request is queued.*%
+ %reference: .+%
+ EOE
+ }
+
: diff-configs
:
{
@@ -1009,7 +1046,7 @@ windows = ($cxx.target.class == 'windows')
# Suppress the --yes option.
#
- test.arguments = $regex.apply($test.arguments, '^(--yes)$', '');
+ test.arguments = $regex.apply($test.arguments, '^--yes$', '');
$* <'y' 2>>~"%EOE%"
submitting:
diff --git a/tests/publish.testscript b/tests/publish.testscript
index 41b5eed..688859d 100644
--- a/tests/publish.testscript
+++ b/tests/publish.testscript
@@ -33,7 +33,7 @@ g = [cmdline] 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.22
+# Next version to use: 1.0.25
#
# Normally we disable the progress indication that complicates stderr output
@@ -136,17 +136,39 @@ g = [cmdline] git -C prj >! 2>!
:
{
$new --no-init --no-amalgamation prj 2>- &prj/***;
- sed -i -e 's/^(version:) .*$/\1 12345/' prj/manifest;
+ sed -i -e 's/^(version:) .*$/\1 1.0.22/' prj/manifest;
sed -i \
- -e 's/^(amalgamation =.*)$/\1\nversion = 12345\ndist.package = $project-$version/' \
+ -e 's/^(amalgamation =.*)$/\1\nversion = 1.0.22\ndist.package = $project-$version/' \
-e 's/^using version$//' \
prj/build/bootstrap.build;
$build 'configure:' prj/@prj-cfg/,forward &prj/build/bootstrap/*** 2>!;
- $* --no-progress --forward --section alpha 2>>~%EOE%
- %package submission is queued(: .*prj/12345)?%
+ $* --no-progress --forward --section alpha 2>>~%EOE%;
+ %package submission is queued(: .*prj/1.0.22)?%
+ %reference: .{12}%
+ EOE
+
+ # While at it, test specifying a package name on the command line.
+ #
+ sed -i -e 's/^(version:) .*$/\1 1.0.23/' prj/manifest;
+ sed -i -e 's/^(version =) .*$/\1 1.0.23/' prj/build/bootstrap.build;
+
+ # Suppress the --yes option.
+ #
+ test.arguments = $regex.apply($test.arguments, '^--yes$', '');
+
+ $* --no-progress --forward --section alpha prj <'y' 2>>~"%EOE%"
+ publishing:
+ % to: $repository%
+ as: user <user@example.com>
+ package: prj
+ version: 1.0.23
+ project: prj
+ section: alpha
+ %.?
+ %continue\\?.+ package submission is queued.+%
%reference: .{12}%
EOE
}
@@ -178,6 +200,32 @@ g = [cmdline] git -C prj >! 2>!
EOE
}
+ : pkg-by-name
+ :
+ {
+ $clone_prj;
+ sed -i -e 's/^(version:) .*$/\1 1.0.24/' prj/libprj/manifest;
+ sed -i -e 's/^(version:) .*$/\1 1.0.24/' prj/prj/manifest;
+ $init -C @cfg &prj-cfg/***;
+
+ # Suppress the --yes option.
+ #
+ test.arguments = $regex.apply($test.arguments, '^--yes$', '');
+
+ $* libprj <'y' 2>>~"%EOE%"
+ publishing:
+ % to: $repository%
+ as: user <user@example.com>
+ package: libprj
+ version: 1.0.24
+ project: prj
+ section: stable
+ %.?
+ %continue\\?.+ package submission is queued.+%
+ %reference: .{12}%
+ EOE
+ }
+
: diff-configs
:
{
@@ -241,7 +289,7 @@ g = [cmdline] git -C prj >! 2>!
# Suppress the --yes option.
#
- test.arguments = $regex.apply($test.arguments, '^(--yes)$', '');
+ test.arguments = $regex.apply($test.arguments, '^--yes$', '');
$* <'y' 2>>~"%EOE%"
publishing:
diff --git a/tests/status.testscript b/tests/status.testscript
index 10480ac..c32ea88 100644
--- a/tests/status.testscript
+++ b/tests/status.testscript
@@ -102,8 +102,8 @@ config += -d prj 2>!
]
EOO
- $* libbar 2>>EOE;
- info: no packages initialized in configuration @cfg, skipping
+ $* libbar 2>>/"EOE" != 0;
+ error: no packages in project $~/prj/
EOE
$* --stdout-format 'json' libbar >>~%EOO%
@@ -323,3 +323,178 @@ config += -d prj 2>!
drop libprj
EOE
}
+
+: pkg-by-name
+:
+{
+ $new -t empty prj &prj/***;
+
+ $new --package pkg1 -d prj;
+ $new --package pkg2 -d prj;
+
+ $init -C @cfg $config_cxx -d prj/pkg1 &prj-cfg/***;
+
+ $new -t lib libprj &libprj/***;
+
+ cat <<EOI >+prj/repositories.manifest;
+ :
+ role: prerequisite
+ location: ../libprj
+ type: dir
+ EOI
+
+ cat <<EOI >+prj/pkg1/manifest;
+ depends: libprj
+ EOI
+
+ $sync;
+
+ # Initialized package is specified.
+ #
+ $* -d prj/pkg1 >>EOO;
+ pkg1 configured 0.1.0-a.0.19700101000000#1
+ EOO
+
+ $* pkg1 --stdout-format 'json' >>~%EOO%;
+ [
+ {
+ "configuration": {
+ "id": 1,
+ % "path": ".+prj-cfg",%
+ "name": "cfg"
+ },
+ "packages": [
+ {
+ "name": "pkg1",
+ "status": "configured",
+ "version": "0.1.0-a.0.19700101000000#1",
+ "hold_package": true,
+ "hold_version": true
+ }
+ ]
+ }
+ ]
+ EOO
+
+ # Not initialized package is specified.
+ #
+ $* pkg2 >>EOO;
+ pkg2 available 0.1.0-a.0.19700101000000
+ EOO
+
+ $* pkg2 --stdout-format 'json' >>~%EOO%;
+ [
+ {
+ "configuration": {
+ "id": 1,
+ % "path": ".+prj-cfg",%
+ "name": "cfg"
+ },
+ "packages": [
+ {
+ "name": "pkg2",
+ "status": "available",
+ "available_versions": [
+ {
+ "version": "0.1.0-a.0.19700101000000"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ EOO
+
+ # Dependency package is specified.
+ #
+ $* libprj >>EOO;
+ libprj configured 0.1.0-a.0.19700101000000
+ EOO
+
+ $* libprj --stdout-format 'json' >>~%EOO%;
+ [
+ {
+ "configuration": {
+ "id": 1,
+ % "path": ".+prj-cfg",%
+ "name": "cfg"
+ },
+ "packages": [
+ {
+ "name": "libprj",
+ "status": "configured",
+ "version": "0.1.0-a.0.19700101000000"
+ }
+ ]
+ }
+ ]
+ EOO
+
+ # Unknown dependency package is specified.
+ #
+ $* libprj1 >>EOO;
+ libprj1 unknown
+ EOO
+
+ $* libprj1 --stdout-format 'json' >>~%EOO%;
+ [
+ {
+ "configuration": {
+ "id": 1,
+ % "path": ".+prj-cfg",%
+ "name": "cfg"
+ },
+ "packages": [
+ {
+ "name": "libprj1",
+ "status": "unknown"
+ }
+ ]
+ }
+ ]
+ EOO
+
+ # No packages are specified.
+ #
+ $* >>EOO;
+ pkg1 configured 0.1.0-a.0.19700101000000#1
+ EOO
+
+ $* --stdout-format 'json' >>~%EOO%;
+ [
+ {
+ "configuration": {
+ "id": 1,
+ % "path": ".+prj-cfg",%
+ "name": "cfg"
+ },
+ "packages": [
+ {
+ "name": "pkg1",
+ "status": "configured",
+ "version": "0.1.0-a.0.19700101000000#1",
+ "hold_package": true,
+ "hold_version": true
+ },
+ {
+ "name": "pkg2",
+ "status": "uninitialized"
+ }
+ ]
+ }
+ ]
+ EOO
+
+ # Both initialized and dependency packages are specified.
+ #
+ $* libprj pkg1 -d prj 2>>EOE != 0;
+ error: initialized package pkg1 specified with dependency package libprj
+ EOE
+
+ $deinit 2>>/"EOE"
+ deinitializing in project $~/prj/
+ synchronizing:
+ drop pkg1
+ drop libprj
+ EOE
+}
diff --git a/tests/sync.testscript b/tests/sync.testscript
index 9f3e381..253e026 100644
--- a/tests/sync.testscript
+++ b/tests/sync.testscript
@@ -205,6 +205,67 @@ deinit += -d prj
EOE
}
+: pkg-by-name
+:
+{
+ $new -t empty prj &prj/***;
+
+ $new --package pkg1 -d prj;
+ $new --package pkg2 -d prj;
+
+ init += -d prj;
+
+ $init -C @cfg $config_cxx &prj-cfg/***;
+
+ $new -t lib libprj &libprj/***;
+
+ cat <<EOI >+prj/repositories.manifest;
+ :
+ role: prerequisite
+ location: ../libprj
+ type: dir
+ EOI
+
+ cat <<EOI >+prj/pkg1/manifest;
+ depends: libprj
+ EOI
+
+ cat <<EOI >+prj/pkg2/manifest;
+ tags: c++
+ EOI
+
+ $* pkg1 -d prj/pkg2 ?libprj 2>>~%EOE%;
+ %.+
+ synchronizing:
+ new libprj/0.1.0-a.0.19700101000000 (required by pkg1)
+ upgrade pkg1/0.1.0-a.0.19700101000000#1
+ upgrade pkg2/0.1.0-a.0.19700101000000#1
+ EOE
+
+ sed -i -e 's/^(version:).+$/\1 1.0.0/' libprj/manifest;
+
+ $* libprj -f -d prj 2>>~%EOE%;
+ %.+
+ synchronizing:
+ upgrade libprj/1.0.0
+ reconfigure pkg1/0.1.0-a.0.19700101000000#1
+ EOE
+
+ $* libprj pkg1 -d prj 2>>EOE != 0;
+ error: initialized package pkg1 specified with dependency package libprj
+ EOE
+
+ $deinit 2>>/"EOE"
+ deinitializing in project $~/prj/
+ deinitializing package pkg1
+ deinitializing package pkg2
+ synchronizing:
+ drop pkg1
+ drop libprj
+ drop pkg2
+ EOE
+}
+
: config-vars
:
{
diff --git a/tests/update.testscript b/tests/update.testscript
index 528a12b..a5689b6 100644
--- a/tests/update.testscript
+++ b/tests/update.testscript
@@ -135,3 +135,45 @@ deinit += -d prj
drop pkg1
EOE
}
+
+: pkg-by-name
+:
+{
+ $new -t empty prj &prj/***;
+
+ $new --package pkg1 -d prj;
+ $new --package pkg2 -d prj;
+
+ $init -C @cfg &prj-cfg/***;
+
+ $* pkg3 -d prj/ 2>>/"EOE" != 0;
+ error: no package pkg3 in project $~/prj/
+ EOE
+
+ $* pkg1 -d prj/pkg2 2>>~%EOE%;
+ %(mkdir|c\+\+|ld|ln) .+%{8}
+ EOE
+
+ # @@ TMP Strangely, get re-symlinked on Windows (every time if run this
+ # command multiple times).
+ #
+ $* pkg1 -d prj/ 2>>~%EOE%;
+ %ln .+pkg1.+ -> .+pkg1.+%?
+ %info: .+pkg1.+ is up to date%
+ EOE
+
+ $deinit pkg3 -d prj 2>>/"EOE" != 0;
+ error: no package pkg3 in project $~/prj/
+ EOE
+
+ # While at it, test specifying a package name on the deinit command line.
+ #
+ $deinit pkg1 -d prj/pkg2 2>>/"EOE"
+ deinitializing in project $~/prj/
+ deinitializing package pkg1
+ deinitializing package pkg2
+ synchronizing:
+ drop pkg1
+ drop pkg2
+ EOE
+}