From 1d85dec79e6b5dab3b1a7988f95603a8918e2993 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 26 Nov 2021 18:40:23 +0300 Subject: Add support for submitting for CI packages with non-standard version --- bdep/ci.cli | 21 +++++- bdep/ci.cxx | 183 ++++++++++++++++++++++++++++++++++------------------ tests/ci.testscript | 31 ++++++++- 3 files changed, 170 insertions(+), 65 deletions(-) diff --git a/bdep/ci.cli b/bdep/ci.cli index 5f955ea..17e5cea 100644 --- a/bdep/ci.cli +++ b/bdep/ci.cli @@ -20,7 +20,7 @@ namespace bdep \c{\b{bdep ci} [] [] []} - \c{ = (\b{@} | \b{--config}|\b{-c} )... | \b{--all}|\b{-a}\n + \c{ = (\b{@} | \b{--config}|\b{-c} )... | \b{--all}|\b{-a} | \b{--forward}\n = (\b{--directory}|\b{-d} )... | \n = \b{--directory}|\b{-d} } @@ -33,8 +33,9 @@ namespace bdep directory is assumed. If no configuration is specified, then the default 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. + submitted, unless the \cb{--forward} option is specified (see below). 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 @@ -90,6 +91,14 @@ namespace bdep service, normally, the CI server will respond with a reference that can be used to query the results. See \l{brep#ci Package CI} for details on the CI request handling. + + If the \cb{--forward} option is specified then the forwarded + configurations are used instead of the default configurations. In + particular, this means that in this mode the project doesn't need to be + initialized and all that's required is for package's source directories + to be configured to forward to an out of source build configuration (see + \l{b(1)} for details on forwarded configurations). This, for example, can + be used to submit packages that don't use the standard version. " } @@ -190,6 +199,12 @@ namespace bdep \cb{success}. For other recognized outcomes refer to the CI service documentation." } + + bool --forward + { + "Use the forwarded configuration for each package instead of the + default configuration." + } }; " diff --git a/bdep/ci.cxx b/bdep/ci.cxx index 216c598..7971688 100644 --- a/bdep/ci.cxx +++ b/bdep/ci.cxx @@ -178,6 +178,15 @@ namespace bdep { tracer trace ("ci"); + if (o.forward ()) + { + if (const char* n = (o.config_name_specified () ? "@" : + o.config_id_specified () ? "--config-id" : + o.config_specified () ? "--config|-c" : + o.all () ? "--all|-a" : nullptr)) + fail << n << " specified together with --forward"; + } + vector overrides; auto override = [&overrides] (string n, string v) @@ -265,102 +274,154 @@ namespace bdep // 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 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 + // cases we also verify that for each package only one configuration in + // which it is initialized 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). // + // 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. // // 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 */, - false /* load_packages */)); + false /* ignore_packages */, + o.forward () /* load_packages */)); const dir_path& prj (pp.project); - configurations cfgs; - { - // Don't keep the database open longer than necessary. - // - database db (open (prj, trace)); - - transaction t (db.begin ()); - cfgs = find_configurations (o, prj, t).first; - t.commit (); - } - - // Collect package names, versions, and configurations used. + // Collect package names, versions, and configurations used (except for + // the forward mode). // struct package { package_name name; - standard_version version; - shared_ptr config; + string version; + shared_ptr config; // NULL in the forward mode. }; vector pkgs; - // 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) + if (o.forward ()) { - auto i (find_if (pkgs.begin (), - pkgs.end (), - [&n] (const package& p) {return p.name == n;})); - - if (i != pkgs.end ()) + // Add a package to the list and suppressing duplicates. + // + auto add_package = [&o, &pkgs] (package_name n, const dir_path& d) { - if (i->config == c) + auto i (find_if (pkgs.begin (), + pkgs.end (), + [&n] (const package& p) {return p.name == n;})); + + if (i != pkgs.end ()) return; - fail << "package " << n << " is initialized in multiple specified " - << "configurations" << - info << *i->config << - info << *c; - } + package_info pi (package_b_info (o, d, false /* ext_mods */)); - standard_version v (package_version (o, c->path, n)); - pkgs.push_back (package {move (n), move (v), move (c)}); - }; + // Verify the package version, unless it is standard and thus is + // already verified. + // + if (pi.version.empty ()) + try + { + bpkg::version (pi.version_string); + } + catch (const invalid_argument& e) + { + fail << "invalid package " << n << " version '" << pi.version_string + << "': " << e << + info << "package source directory is " << d; + } - if (pp.packages.empty ()) - { - for (const shared_ptr& c: cfgs) + pkgs.push_back (package {move (n), move (pi.version_string), nullptr}); + }; + + for (package_location& p: pp.packages) { - for (const package_state& p: c->packages) - add_package (p.name, c); + dir_path d (pp.project / p.path); + package_info pi (package_b_info (o, d, false /* ext_mods */)); + + if (pi.src_root == pi.out_root) + fail << "package " << p.name << " source directory is not forwarded" << + info << "package source directory is " << d; + + add_package (p.name, d); } } else { - for (package_location& p: pp.packages) + configurations cfgs; + { + // Don't keep the database open longer than necessary. + // + database db (open (prj, trace)); + + transaction t (db.begin ()); + cfgs = find_configurations (o, prj, t).first; + t.commit (); + } + + // 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) { - bool init (false); + 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), v.string (), move (c)}); + }; + if (pp.packages.empty ()) + { 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. - // + for (const package_state& p: c->packages) add_package (p.name, c); - init = true; - } } + } + else + { + for (package_location& p: pp.packages) + { + bool init (false); - if (!init) - fail << "package " << p.name << " is not initialized in any " - << "configuration"; + 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"; + } } } @@ -470,7 +531,7 @@ namespace bdep for (const package& p: pkgs) params.push_back ({parameter::text, "package", - p.name.string () + '/' + p.version.string ()}); + p.name.string () + '/' + p.version}); if (ibpk) params.push_back ({parameter::text, "interactive", move (*ibpk)}); diff --git a/tests/ci.testscript b/tests/ci.testscript index 2348cce..f048de0 100644 --- a/tests/ci.testscript +++ b/tests/ci.testscript @@ -40,7 +40,7 @@ g = git -C prj 2>! >&2 repository='http://example.com/prj.git' test.arguments += --yes --repository "$repository" --server "$server" \ ---simulate 'success' + --simulate 'success' config_cxx = cc config.cxx=$quote($recall($cxx.path) $cxx.config.mode, true) @@ -358,6 +358,35 @@ windows = ($cxx.target.class == 'windows') EOE } } + + : non-standard-version + : + : Test submitting a package with the non-standard version from a + : non-bdep-initialized project, using the forwarded build2 configuration. + : + { + cp --no-cleanup -pr ../../prj ./ &prj/***; + rm -r prj/.bdep/; + + sed -i -e 's/^(version:) .*$/\1 12345/' prj/manifest; + + sed -i \ + -e 's/^(project =.*)$/\1\nversion = 12345/' \ + -e 's/^using version$//' \ + prj/build/bootstrap.build; + + $g branch non-standard-version; + $g checkout non-standard-version; + $g -c core.safecrlf=false commit -a -m 'Change version'; + $g push --set-upstream origin non-standard-version; + + $build 'configure:' prj/@prj-cfg/,forward &prj/build/bootstrap/*** 2>!; + + $* --no-progress --forward 2>>~%EOE% + %CI request is queued.*% + %reference: .+% + EOE + } } : multi-pkg -- cgit v1.1