diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2023-03-24 20:32:25 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2023-04-03 16:52:41 +0300 |
commit | 8c54405d78b87b8756106eceec0a53ef0225d05e (patch) | |
tree | 3e94822ac2234814190297e571e41ac90615a51a /bbot/worker/worker.cxx | |
parent | 2cdf8357b2eb3ddaf726c2cd51bff524e8faa3d6 (diff) |
Add support for package build config worker steps and steps enabling/disabling
Diffstat (limited to 'bbot/worker/worker.cxx')
-rw-r--r-- | bbot/worker/worker.cxx | 2527 |
1 files changed, 1482 insertions, 1045 deletions
diff --git a/bbot/worker/worker.cxx b/bbot/worker/worker.cxx index 6c96b5b..f1e5e8c 100644 --- a/bbot/worker/worker.cxx +++ b/bbot/worker/worker.cxx @@ -151,6 +151,9 @@ catch (const system_error& e) // Step IDs. // +// NOTE: keep ids ordered according to the sequence of steps and remember to +// update unreachable breakpoint checks if changing anything here. +// enum class step_id { // Common fallbacks for bpkg_*_create/b_test_installed_create and @@ -302,6 +305,12 @@ static const strings step_id_str { "end"}; +static inline const string& +to_string (step_id s) +{ + return step_id_str[static_cast<size_t> (s)]; +} + using std::regex; namespace regex_constants = std::regex_constants; using regexes = vector<regex>; @@ -390,7 +399,7 @@ run_cmd (step_id step, auto prompt_step = [step, &t, &log, &bkp_step, &prompt] () { - const string& sid (step_id_str[static_cast<size_t> (step)]); + const string& sid (to_string (step)); // Prompt the user if the breakpoint is reached. // @@ -789,7 +798,7 @@ upload_manifest (tracer& trace, } } -static const string worker_checksum ("4"); // Logic version. +static const string worker_checksum ("5"); // Logic version. static int bbot:: build (size_t argc, const char* argv[]) @@ -915,53 +924,105 @@ build (size_t argc, const char* argv[]) } // Split the argument into prefix (empty if not present) and unquoted - // value. Return nullopt if the prefix is invalid. + // value (absent if not present) and determine the step status. If the + // prefix is present and is prepended with the '+'/'-' character, then the + // respective step needs to be enabled/disabled. Return nullopt if the + // prefix is invalid. // - auto parse_arg = [] (const string& a) -> optional<pair<string, string>> + // Note that arguments with absent values are normally used to + // enable/disable steps and are omitted from the command lines. + // + struct argument + { + string prefix; + + // Absent if the argument value is an empty unquoted string. + // + optional<string> value; + + // True - enable, false - disable, nullopt - neutral. + // + optional<bool> step_status; + }; + + auto parse_arg = [] (const string& a) -> optional<argument> { size_t p (a.find_first_of (":=\"'")); + auto value = [] (const string& v) + { + return !v.empty () ? unquote (v) : optional<string> (); + }; + if (p == string::npos || a[p] != ':') // No prefix. - return make_pair (string (), unquote (a)); + return argument {string (), value (a), nullopt}; + + string prefix (a, 0, p); + + optional<bool> enable; + if (prefix[0] == '+' || prefix[0] == '-') + { + enable = (prefix[0] == '+'); + + prefix.erase (0, 1); + + if (prefix != "bpkg.update" && + prefix != "bpkg.test" && + prefix != "bpkg.test-separate.update" && + prefix != "bpkg.test-separate.test" && + prefix != "bpkg.install" && + prefix != "b.test-installed.test" && + prefix != "bpkg.test-separate-installed.update" && + prefix != "bpkg.test-separate-installed.test") + { + return nullopt; // Prefix is invalid. + } + } for (const string& id: step_id_str) { - if (a.compare (0, p, id, 0, p) == 0 && - (id.size () == p || (id.size () > p && id[p] == '.'))) - return make_pair (a.substr (0, p), unquote (a.substr (p + 1))); + size_t n (prefix.size ()); + if (id.compare (0, n, prefix) == 0 && + (id.size () == n || (id.size () > n && id[n] == '.'))) + return argument {move (prefix), value (a.substr (p + 1)), enable}; } return nullopt; // Prefix is invalid. }; - // Parse configuration arguments. Report failures to the bbot controller. + // Keep track of explicitly enabled/disabled steps. // - std::multimap<string, string> tgt_args; + std::map<string, bool> step_statuses; - for (const string& c: tm.target_config) + // Return true if the step is explicitly enabled via a +<prefix>:[<value>] + // environment/configuration argument. + // + // @@ TMP Use it for bpkg.bindist step. + // +#if 0 + auto step_enabled = [&step_statuses] (step_id step) -> bool { - optional<pair<string, string>> v (parse_arg (c)); - - if (!v) - { - rm.status |= result_status::abort; - l3 ([&]{trace << "invalid configuration argument prefix in " - << "'" << c << "'";}); - break; - } - - if (v->second[0] != '-' && v->second.find ('=') == string::npos) - { - rm.status |= result_status::abort; - l3 ([&]{trace << "invalid configuration argument '" << c << "'";}); - break; - } + auto i (step_statuses.find (to_string (step))); + return i != step_statuses.end () && i->second; + }; +#endif - tgt_args.emplace (move (*v)); - } + // Return true if the step is explicitly disabled via a -<prefix>:[<value>] + // environment/configuration argument. + // + auto step_disabled = [&step_statuses] (step_id step) -> bool + { + auto i (step_statuses.find (to_string (step))); + return i != step_statuses.end () && !i->second; + }; - if (!rm.status) - break; + // Parse the environment, target configuration, and build package + // configuration arguments. + // + // NOTE: keep this parsing order intact so that, for example, a build + // package configuration argument can override step status specified + // by a target configuration argument. + // // Parse environment arguments. // @@ -971,147 +1032,73 @@ build (size_t argc, const char* argv[]) for (size_t i (1); i != argc; ++i) { const char* a (argv[i]); - optional<pair<string, string>> v (parse_arg (a)); + optional<argument> v (parse_arg (a)); if (!v) fail << "invalid environment argument prefix in '" << a << "'"; - bool mod (v->second[0] != '-' && v->second.find ('=') == string::npos); - - if (mod && !v->first.empty () && - v->first != "b.create" && - v->first != "bpkg.create" && - v->first != "bpkg.target.create" && - v->first != "bpkg.host.create" && - v->first != "bpkg.module.create" && - v->first != "b.test-installed.create" && - v->first != "bpkg.test-separate-installed.create" && - v->first != "bpkg.test-separate-installed.create_for_target" && - v->first != "bpkg.test-separate-installed.create_for_host" && - v->first != "bpkg.test-separate-installed.create_for_module") + bool mod (v->value && + (*v->value)[0] != '-' && + v->value->find ('=') == string::npos); + + if (mod && + !v->prefix.empty () && + v->prefix != "b.create" && + v->prefix != "bpkg.create" && + v->prefix != "bpkg.target.create" && + v->prefix != "bpkg.host.create" && + v->prefix != "bpkg.module.create" && + v->prefix != "b.test-installed.create" && + v->prefix != "bpkg.test-separate-installed.create" && + v->prefix != "bpkg.test-separate-installed.create_for_target" && + v->prefix != "bpkg.test-separate-installed.create_for_host" && + v->prefix != "bpkg.test-separate-installed.create_for_module") fail << "invalid module prefix in '" << a << "'"; - (mod ? modules : env_args).emplace (move (*v)); + if (v->step_status) + step_statuses[v->prefix] = *v->step_status; + + if (v->value) + (mod ? modules : env_args).emplace (make_pair (move (v->prefix), + move (*v->value))); } - // Return command arguments for the specified step id, complementing - // *.create[_for_*] steps with un-prefixed arguments. If no arguments are - // specified for the step then use the specified fallbacks, potentially - // both. Arguments with more specific prefixes come last. + // Parse target configuration arguments. Report failures to the bbot + // controller. // - auto step_args = [] (const std::multimap<string, string>& args, - step_id step, - optional<step_id> fallback1 = nullopt, - optional<step_id> fallback2 = nullopt) -> cstrings - { - cstrings r; - - // Add arguments for a specified, potentially empty, prefix. - // - auto add_args = [&args, &r] (const string& prefix) - { - auto range (args.equal_range (prefix)); + std::multimap<string, string> tgt_args; - for (auto i (range.first); i != range.second; ++i) - r.emplace_back (i->second.c_str ()); - }; + for (const string& c: tm.target_config) + { + optional<argument> v (parse_arg (c)); - // Add un-prefixed arguments if this is one of the *.create[_for_*] - // steps. - // - switch (step) + if (!v) { - case step_id::b_create: - case step_id::bpkg_create: - case step_id::bpkg_target_create: - case step_id::bpkg_host_create: - case step_id::bpkg_module_create: - case step_id::b_test_installed_create: - case step_id::bpkg_test_separate_installed_create: - case step_id::bpkg_test_separate_installed_create_for_target: - case step_id::bpkg_test_separate_installed_create_for_host: - case step_id::bpkg_test_separate_installed_create_for_module: - { - add_args (""); - break; - } - default: break; + rm.status |= result_status::abort; + l3 ([&]{trace << "invalid target configuration argument prefix in " + << "'" << c << "'";}); + break; } - auto add_step_args = [&add_args] (step_id step) - { - const string& s (step_id_str[static_cast<size_t> (step)]); - - for (size_t n (0);; ++n) - { - n = s.find ('.', n); - - add_args (n == string::npos ? s : string (s, 0, n)); - - if (n == string::npos) - break; - } - }; - - // If no arguments found for the step id, then use the fallback step - // ids, if specified. - // - if (args.find (step_id_str[static_cast<size_t> (step)]) != args.end ()) - { - add_step_args (step); - } - else + if (v->value && + (*v->value)[0] != '-' && + v->value->find ('=') == string::npos) { - // Note that if we ever need to specify fallback pairs with common - // ancestors, we may want to suppress duplicate ancestor step ids. - // - if (fallback1) - add_step_args (*fallback1); - - if (fallback2) - add_step_args (*fallback2); + rm.status |= result_status::abort; + l3 ([&]{trace << "invalid target configuration argument '" << c + << "'";}); + break; } - return r; - }; + if (v->step_status) + step_statuses[v->prefix] = *v->step_status; - // bpkg-rep-fetch trust options. - // - cstrings trust_ops; - { - const char* t ("--trust-no"); - - for (const string& fp: tm.trust) - { - if (fp == "yes") - t = "--trust-yes"; - else - { - trust_ops.push_back ("--trust"); - trust_ops.push_back (fp.c_str ()); - } - } - - trust_ops.push_back (t); + if (v->value) + tgt_args.emplace (make_pair (move (v->prefix), move (*v->value))); } - const string& pkg (tm.name.string ()); - const version& ver (tm.version); - const string repo (tm.repository.string ()); - const dir_path pkg_dir (pkg + '-' + ver.string ()); - const string pkg_var (tm.name.variable ()); - - // Specify the revision explicitly for the bpkg-build command not to end - // up with a race condition building the latest revision rather than the - // zero revision. - // - const string pkg_rev (pkg + - '/' + - version (ver.epoch, - ver.upstream, - ver.release, - ver.effective_revision (), - ver.iteration).string ()); + if (!rm.status) + break; // Parse the build package configuration represented as a whitespace // separated list of the following potentially quoted bpkg-pkg-build @@ -1120,12 +1107,21 @@ build (size_t argc, const char* argv[]) // <option>... <config-var>... ({ <config-var>... }+ (?[sys:]|sys:)<pkg-name>[<version-spec>])... // // If the package configuration is specified, then parse it into the - // options list, the main package-specific configuration variables list, - // and the dependency packages list, potentially with their own - // configuration variables (but not options). + // prefixed global options and configuration variables map, unprefixed + // global options list, the main package-specific configuration variables + // list, and the dependency packages list, potentially with their own + // configuration variables (but not options). The prefixed arguments are + // added to the command lines at the corresponding steps after potential + // environment and target configuration arguments. Unprefixed arguments + // are added to the bpkg-pkg-build command line at the + // bpkg.configure.build step. Specifically, the unprefixed global options + // are specified after all the prefixed global options and the unprefixed + // variables are specified for the main package only, wherever it is + // configured. // - strings pkg_config_opts; - strings pkg_config_vars; + std::multimap<string, string> pkg_args; + strings pkg_config_opts; + strings pkg_config_vars; vector<pair<string, strings>> pkg_config_deps; if (!tm.package_config.empty ()) @@ -1148,13 +1144,47 @@ build (size_t argc, const char* argv[]) try { strings argsv (string_parser::parse_quoted (tm.package_config, - true /* unquote */)); + false /* unquote */)); cli::vector_scanner scanv (argsv); cli::group_scanner args (scanv); while (args.more ()) { + string a (args.next ()); + + // Unless the argument is an unquoted dependency (starts with `?` or + // `sys:`), first try to interpret it as a prefixed option/variable + // and/or step id status (enabled/disabled). + // + if (!(a[0] == '?' || a.compare (0, 4, "sys:") == 0)) + { + optional<argument> v (parse_arg (a)); + + if (v && !v->prefix.empty ()) + { + if (v->value && + (*v->value)[0] != '-' && + v->value->find ('=') == string::npos) + fail ("invalid prefixed argument '" + a + '\''); + + if (args.group ().more ()) + fail ("unexpected options group for prefixed argument '" + a + + '\''); + + if (v->step_status) + step_statuses[v->prefix] = *v->step_status; + + if (v->value) + pkg_args.emplace (make_pair (move (v->prefix), + move (*v->value))); + + continue; + } + } + + a = unquote (a); + // Return true if the argument is an option. // // Note that options with values can only be specified using @@ -1180,7 +1210,6 @@ build (size_t argc, const char* argv[]) a.find ('=') != string::npos; }; - string a (args.next ()); bool o (opt (a)); bool v (var (a)); @@ -1214,7 +1243,7 @@ build (size_t argc, const char* argv[]) strings vars; while (ag.more ()) { - string da (ag.next ()); + string da (unquote (ag.next ())); if (!var (da)) fail ("argument is not a configuration variable for " "dependency " + a + ": '" + da + '\''); @@ -1241,6 +1270,125 @@ build (size_t argc, const char* argv[]) } } + // Return command arguments for the specified step id, complementing + // *.create[_for_*] steps with un-prefixed arguments. If no arguments are + // specified for the step then use the specified fallbacks, potentially + // both. Arguments with more specific prefixes come last. + // + auto step_args = [] (const std::multimap<string, string>& args, + step_id step, + optional<step_id> fallback1 = nullopt, + optional<step_id> fallback2 = nullopt) -> cstrings + { + cstrings r; + + // Add arguments for a specified, potentially empty, prefix. + // + auto add_args = [&args, &r] (const string& prefix) + { + auto range (args.equal_range (prefix)); + + for (auto i (range.first); i != range.second; ++i) + r.emplace_back (i->second.c_str ()); + }; + + // Add un-prefixed arguments if this is one of the *.create[_for_*] + // steps. + // + switch (step) + { + case step_id::b_create: + case step_id::bpkg_create: + case step_id::bpkg_target_create: + case step_id::bpkg_host_create: + case step_id::bpkg_module_create: + case step_id::b_test_installed_create: + case step_id::bpkg_test_separate_installed_create: + case step_id::bpkg_test_separate_installed_create_for_target: + case step_id::bpkg_test_separate_installed_create_for_host: + case step_id::bpkg_test_separate_installed_create_for_module: + { + add_args (""); + break; + } + default: break; + } + + auto add_step_args = [&add_args] (step_id step) + { + const string& s (to_string (step)); + + for (size_t n (0);; ++n) + { + n = s.find ('.', n); + + add_args (n == string::npos ? s : string (s, 0, n)); + + if (n == string::npos) + break; + } + }; + + // If no arguments found for the step id, then use the fallback step + // ids, if specified. + // + if (args.find (to_string (step)) != args.end ()) + { + add_step_args (step); + } + else + { + // Note that if we ever need to specify fallback pairs with common + // ancestors, we may want to suppress duplicate ancestor step ids. + // + if (fallback1) + add_step_args (*fallback1); + + if (fallback2) + add_step_args (*fallback2); + } + + return r; + }; + + // bpkg-rep-fetch trust options. + // + cstrings trust_ops; + { + const char* t ("--trust-no"); + + for (const string& fp: tm.trust) + { + if (fp == "yes") + t = "--trust-yes"; + else + { + trust_ops.push_back ("--trust"); + trust_ops.push_back (fp.c_str ()); + } + } + + trust_ops.push_back (t); + } + + const string& pkg (tm.name.string ()); + const version& ver (tm.version); + const string repo (tm.repository.string ()); + const dir_path pkg_dir (pkg + '-' + ver.string ()); + const string pkg_var (tm.name.variable ()); + + // Specify the revision explicitly for the bpkg-build command not to end + // up with a race condition building the latest revision rather than the + // zero revision. + // + const string pkg_rev (pkg + + '/' + + version (ver.epoch, + ver.upstream, + ver.release, + ver.effective_revision (), + ver.iteration).string ()); + // Query the project's build system information with `b info`. // auto prj_info = [&trace] (const dir_path& d, @@ -1588,7 +1736,9 @@ build (size_t argc, const char* argv[]) // // Create the target configuration. // - // bpkg create <env-modules> <env-config-args> <tgt-config-args> + // bpkg create <env-modules> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> // if (create_target) { @@ -1607,7 +1757,8 @@ build (size_t argc, const char* argv[]) !target_pkg ? cstrings ({"--uuid", target_uuid}) : cstrings (), step_args (modules, s, f1, f2), step_args (env_args, s, f1, f2), - step_args (tgt_args, s, f1, f2)); + step_args (tgt_args, s, f1, f2), + step_args (pkg_args, s, f1, f2)); if (!r.status) break; @@ -1628,7 +1779,9 @@ build (size_t argc, const char* argv[]) step_id f1 (step_id::b_create); step_id f2 (step_id::bpkg_create); - // bpkg create --type host <env-modules> <env-config-args> <tgt-config-args> + // bpkg create --type host <env-modules> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> // r.status |= run_bpkg ( b, @@ -1641,7 +1794,8 @@ build (size_t argc, const char* argv[]) "--uuid", host_uuid, step_args (modules, s, f1, f2), step_args (env_args, s, f1, f2), - step_args (tgt_args, s, f1, f2)); + step_args (tgt_args, s, f1, f2), + step_args (pkg_args, s, f1, f2)); if (!r.status) break; @@ -1649,7 +1803,9 @@ build (size_t argc, const char* argv[]) // Create the install configuration. // - // bpkg create <env-modules> <env-config-args> <tgt-config-args> + // bpkg create <env-modules> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> // if (create_install) { @@ -1667,7 +1823,8 @@ build (size_t argc, const char* argv[]) "--uuid", install_uuid, step_args (modules, s, f1, f2), step_args (env_args, s, f1, f2), - step_args (tgt_args, s, f1, f2)); + step_args (tgt_args, s, f1, f2), + step_args (pkg_args, s, f1, f2)); if (!r.status) break; @@ -1718,7 +1875,9 @@ build (size_t argc, const char* argv[]) // Create the module configuration. // { - // b create(<dir>) config.config.load=~build2 [<env-config-args> <tgt-config-args>] + // b create(<dir>) config.config.load=~build2 [<env-config-args> + // <tgt-config-args> + // <pkg-config-args>] // // Note also that we suppress warnings about unused config.* values. // @@ -1731,6 +1890,7 @@ build (size_t argc, const char* argv[]) string mods; cstrings eas; cstrings cas; + cstrings pas; if (module_pkg && selfhost) { @@ -1746,6 +1906,7 @@ build (size_t argc, const char* argv[]) eas = step_args (env_args, s); cas = step_args (tgt_args, s); + pas = step_args (pkg_args, s); } else mods = "cc"; @@ -1759,7 +1920,8 @@ build (size_t argc, const char* argv[]) "config.config.load=~build2", "config.config.persist+='config.*'@unused=drop", eas, - cas); + cas, + pas); if (!r.status) break; @@ -1796,7 +1958,9 @@ build (size_t argc, const char* argv[]) mods += m; } - // b create(<dir>) config.config.load=~build2 [<env-config-args> <tgt-config-args>] + // b create(<dir>) config.config.load=~build2 [<env-config-args> + // <tgt-config-args> + // <pkg-config-args>] // r.status |= run_b ( b, @@ -1807,7 +1971,8 @@ build (size_t argc, const char* argv[]) "config.config.load=~build2", "config.config.persist+='config.*'@unused=drop", step_args (env_args, s), - step_args (tgt_args, s)); + step_args (tgt_args, s), + step_args (pkg_args, s)); if (!r.status) break; @@ -1926,7 +2091,8 @@ build (size_t argc, const char* argv[]) // configuration for external build-time tests, if any, and the install // configuration, if present. // - // bpkg add <env-config-args> <tgt-config-args> <repository-url> + // bpkg add <env-config-args> <tgt-config-args> <pkg-config-args> + // <repository-url> // { step_id b (step_id::bpkg_configure_add); @@ -1941,13 +2107,15 @@ build (size_t argc, const char* argv[]) "-d", main_pkg_conf, step_args (env_args, s), step_args (tgt_args, s), + step_args (pkg_args, s), repo); if (!r.status) break; } - // bpkg fetch <env-config-args> <tgt-config-args> <trust-options> + // bpkg fetch <env-config-args> <tgt-config-args> <pkg-config-args> + // <trust-options> // { step_id b (step_id::bpkg_configure_fetch); @@ -1962,6 +2130,7 @@ build (size_t argc, const char* argv[]) "-d", main_pkg_conf, step_args (env_args, s), step_args (tgt_args, s), + step_args (pkg_args, s), trust_ops); if (!r.status) @@ -1970,7 +2139,8 @@ build (size_t argc, const char* argv[]) if (create_install) { - // bpkg add <env-config-args> <tgt-config-args> <repository-url> + // bpkg add <env-config-args> <tgt-config-args> <pkg-config-args> + // <repository-url> // { step_id b (step_id::bpkg_configure_add); @@ -1985,13 +2155,15 @@ build (size_t argc, const char* argv[]) "-d", install_conf, step_args (env_args, s), step_args (tgt_args, s), + step_args (pkg_args, s), repo); if (!r.status) break; } - // bpkg fetch <env-config-args> <tgt-config-args> <trust-options> + // bpkg fetch <env-config-args> <tgt-config-args> <pkg-config-args> + // <trust-options> // { step_id b (step_id::bpkg_configure_fetch); @@ -2006,6 +2178,7 @@ build (size_t argc, const char* argv[]) "-d", install_conf, step_args (env_args, s), step_args (tgt_args, s), + step_args (pkg_args, s), trust_ops); if (!r.status) @@ -2015,7 +2188,8 @@ build (size_t argc, const char* argv[]) if (has_buildtime_tests) { - // bpkg add <env-config-args> <tgt-config-args> <repository-url> + // bpkg add <env-config-args> <tgt-config-args> <pkg-config-args> + // <repository-url> // { step_id b (step_id::bpkg_configure_add); @@ -2030,13 +2204,15 @@ build (size_t argc, const char* argv[]) "-d", target_conf, step_args (env_args, s), step_args (tgt_args, s), + step_args (pkg_args, s), repo); if (!r.status) break; } - // bpkg fetch <env-config-args> <tgt-config-args> <trust-options> + // bpkg fetch <env-config-args> <tgt-config-args> <pkg-config-args> + // <trust-options> // { step_id b (step_id::bpkg_configure_fetch); @@ -2051,6 +2227,7 @@ build (size_t argc, const char* argv[]) "-d", target_conf, step_args (env_args, s), step_args (tgt_args, s), + step_args (pkg_args, s), trust_ops); if (!r.status) @@ -2086,13 +2263,17 @@ build (size_t argc, const char* argv[]) // it simple for now. // strings common_args; - strings pkg_args; + strings pkgs; if (target_pkg) // The simple common case (see above)? { // The overall command looks like this (but some parts may be omitted): // - // bpkg build --configure-only <pkg-config-opts> <env-config-args> <tgt-config-args> -- + // bpkg build --configure-only <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // <pkg-config-opts> + // -- // { <pkg-config-vars>|config.<pkg-name>.develop=false }+ <pkg> // { config.<runtime-test-name>.develop=false }+ <runtime-test>... // { <dep-config-vars> }+ <dep>... @@ -2104,16 +2285,18 @@ build (size_t argc, const char* argv[]) cstrings eas (step_args (env_args, s, f1, f2)); cstrings cas (step_args (tgt_args, s, f1, f2)); + cstrings pas (step_args (pkg_args, s, f1, f2)); common_args.push_back ("--checkout-root"); common_args.push_back (dist_root.string ()); common_args.insert (common_args.end (), eas.begin (), eas.end ()); common_args.insert (common_args.end (), cas.begin (), cas.end ()); + common_args.insert (common_args.end (), pas.begin (), pas.end ()); // Add the main package. // - pkg_args.push_back ("{"); + pkgs.push_back ("{"); // @@ config.<pkg>.develop=false // @@ -2121,16 +2304,16 @@ build (size_t argc, const char* argv[]) // configuration variables specified. // if (!pkg_config_vars.empty ()) - pkg_args.insert (pkg_args.end (), - pkg_config_vars.begin (), pkg_config_vars.end ()); + pkgs.insert (pkgs.end (), + pkg_config_vars.begin (), pkg_config_vars.end ()); #if 1 else - pkg_args.push_back ("config." + pkg_var + ".develop=false"); + pkgs.push_back ("config." + pkg_var + ".develop=false"); #endif - pkg_args.push_back ("}+"); + pkgs.push_back ("}+"); - pkg_args.push_back (pkg_rev); + pkgs.push_back (pkg_rev); // Add the runtime test packages. // @@ -2139,17 +2322,15 @@ build (size_t argc, const char* argv[]) // @@ config.<pkg>.develop=false // #if 1 - pkg_args.push_back ("{"); - pkg_args.push_back ("config." + - t.name.variable () + - ".develop=false"); - pkg_args.push_back ("}+"); + pkgs.push_back ("{"); + pkgs.push_back ("config." + t.name.variable () + ".develop=false"); + pkgs.push_back ("}+"); #endif // Add test dependency package constraints (for example // 'bar > 1.0.0'). // - pkg_args.push_back (t.string ()); + pkgs.push_back (t.string ()); } // Add the main package dependencies. @@ -2158,25 +2339,45 @@ build (size_t argc, const char* argv[]) { if (!d.second.empty ()) { - pkg_args.push_back ("{"); - pkg_args.insert (pkg_args.end (), d.second.begin (), d.second.end ()); - pkg_args.push_back ("}+"); + pkgs.push_back ("{"); + pkgs.insert (pkgs.end (), d.second.begin (), d.second.end ()); + pkgs.push_back ("}+"); } - pkg_args.push_back (d.first); + pkgs.push_back (d.first); } } else { // The overall command looks like this (but some parts may be omitted): // - // bpkg build --configure-only <pkg-config-opts> <env-config-args> <tgt-config-args> -- - // { <build-config> <env-config-args> <tgt-config-args> <pkg-config-vars>|config.<pkg-name>.develop=false }+ <pkg> - // { <build-config> <env-config-args> <tgt-config-args> config.<runtime-test-name>.develop=false }+ <runtime-test>... - // { <install-config> <env-config-args> <tgt-config-args> <pkg-config-vars> }+ <pkg> - // { <target-config> <env-config-args> <tgt-config-args> config.<buildtime-test-name>.develop=false }+ <buildtime-test>... - // { <build-config> <install-config> <dep-config-vars> }+ <dep>... - // { <build-config> <install-config> }+ { <dep>... } + // bpkg build --configure-only <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // <pkg-config-opts> + // -- + // { <build-config> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // <pkg-config-vars>|config.<pkg-name>.develop=false }+ <pkg> + // + // { <build-config> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // config.<runtime-test-name>.develop=false }+ <runtime-test>... + // + // { <install-config> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // <pkg-config-vars> }+ <pkg> + // + // { <target-config> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // config.<buildtime-test-name>.develop=false }+ <buildtime-test>... + // + // { <build-config> <install-config> <dep-config-vars> }+ <dep>... + // { <build-config> <install-config> }+ { <dep>... } // // Main package configuration name. @@ -2198,20 +2399,22 @@ build (size_t argc, const char* argv[]) cstrings eas (step_args (env_args, s, f1, f2)); cstrings cas (step_args (tgt_args, s, f1, f2)); + cstrings pas (step_args (pkg_args, s, f1, f2)); // Add the main package. // { - pkg_args.push_back ("{"); + pkgs.push_back ("{"); - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (conf_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (conf_uuid); - pkg_args.push_back ("--checkout-root"); - pkg_args.push_back (dist_root.string ()); + pkgs.push_back ("--checkout-root"); + pkgs.push_back (dist_root.string ()); - pkg_args.insert (pkg_args.end (), eas.begin (), eas.end ()); - pkg_args.insert (pkg_args.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), eas.begin (), eas.end ()); + pkgs.insert (pkgs.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), pas.begin (), pas.end ()); // @@ config.<pkg>.develop=false // @@ -2219,16 +2422,16 @@ build (size_t argc, const char* argv[]) // package configuration variables specified. // if (!pkg_config_vars.empty ()) - pkg_args.insert (pkg_args.end (), - pkg_config_vars.begin (), pkg_config_vars.end ()); + pkgs.insert (pkgs.end (), + pkg_config_vars.begin (), pkg_config_vars.end ()); #if 1 else - pkg_args.push_back ("config." + pkg_var + ".develop=false"); + pkgs.push_back ("config." + pkg_var + ".develop=false"); #endif - pkg_args.push_back ("}+"); + pkgs.push_back ("}+"); - pkg_args.push_back (pkg_rev); + pkgs.push_back (pkg_rev); } // Add the runtime test packages. @@ -2238,58 +2441,58 @@ build (size_t argc, const char* argv[]) #if 1 for (const auto& t: runtime_tests) { - pkg_args.push_back ("{"); + pkgs.push_back ("{"); - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (conf_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (conf_uuid); - pkg_args.push_back ("--checkout-root"); - pkg_args.push_back (dist_root.string ()); + pkgs.push_back ("--checkout-root"); + pkgs.push_back (dist_root.string ()); if (bootstrap_import) - pkg_args.push_back (*bootstrap_import); + pkgs.push_back (*bootstrap_import); - pkg_args.insert (pkg_args.end (), eas.begin (), eas.end ()); - pkg_args.insert (pkg_args.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), eas.begin (), eas.end ()); + pkgs.insert (pkgs.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), pas.begin (), pas.end ()); - pkg_args.push_back ("config." + - t.name.variable () + - ".develop=false"); + pkgs.push_back ("config." + t.name.variable () + ".develop=false"); - pkg_args.push_back ("}+"); + pkgs.push_back ("}+"); - pkg_args.push_back (t.string ()); + pkgs.push_back (t.string ()); } #else if (has_runtime_tests) { - pkg_args.push_back ("{"); + pkgs.push_back ("{"); - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (conf_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (conf_uuid); - pkg_args.push_back ("--checkout-root"); - pkg_args.push_back (dist_root.string ()); + pkgs.push_back ("--checkout-root"); + pkgs.push_back (dist_root.string ()); if (bootstrap_import) - pkg_args.push_back (*bootstrap_import); + pkgs.push_back (*bootstrap_import); - pkg_args.insert (pkg_args.end (), eas.begin (), eas.end ()); - pkg_args.insert (pkg_args.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), eas.begin (), eas.end ()); + pkgs.insert (pkgs.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), pas.begin (), pas.end ()); - pkg_args.push_back ("}+"); + pkgs.push_back ("}+"); // Add test dependency package constraints and group them if there // are multiple of them. // if (runtime_tests.size () != 1) - pkg_args.push_back ("{"); + pkgs.push_back ("{"); for (const auto& t: runtime_tests) - pkg_args.push_back (t.string ()); + pkgs.push_back (t.string ()); if (runtime_tests.size () != 1) - pkg_args.push_back ("}"); + pkgs.push_back ("}"); } #endif } @@ -2304,6 +2507,7 @@ build (size_t argc, const char* argv[]) cstrings eas (step_args (env_args, s, f1, f2)); cstrings cas (step_args (tgt_args, s, f1, f2)); + cstrings pas (step_args (pkg_args, s, f1, f2)); // Add the main package. // @@ -2312,27 +2516,28 @@ build (size_t argc, const char* argv[]) common_args.push_back ("-d"); common_args.push_back (install_conf.string ()); - pkg_args.push_back ("{"); + pkgs.push_back ("{"); - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (install_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (install_uuid); // Note that we do another re-distribution (with a separate // --checkout-root) in case the package is missing file that // are only used during installation. // - pkg_args.push_back ("--checkout-root"); - pkg_args.push_back (dist_install_root.string ()); + pkgs.push_back ("--checkout-root"); + pkgs.push_back (dist_install_root.string ()); - pkg_args.insert (pkg_args.end (), eas.begin (), eas.end ()); - pkg_args.insert (pkg_args.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), eas.begin (), eas.end ()); + pkgs.insert (pkgs.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), pas.begin (), pas.end ()); - pkg_args.insert (pkg_args.end (), - pkg_config_vars.begin (), pkg_config_vars.end ()); + pkgs.insert (pkgs.end (), + pkg_config_vars.begin (), pkg_config_vars.end ()); - pkg_args.push_back ("}+"); + pkgs.push_back ("}+"); - pkg_args.push_back (pkg_rev); + pkgs.push_back (pkg_rev); } // Add the build-time test packages. @@ -2342,62 +2547,62 @@ build (size_t argc, const char* argv[]) #if 1 for (const auto& t: buildtime_tests) { - pkg_args.push_back ("{"); + pkgs.push_back ("{"); - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (target_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (target_uuid); - pkg_args.push_back ("--checkout-root"); - pkg_args.push_back (dist_root.string ()); + pkgs.push_back ("--checkout-root"); + pkgs.push_back (dist_root.string ()); if (bootstrap_import) - pkg_args.push_back (*bootstrap_import); + pkgs.push_back (*bootstrap_import); - pkg_args.insert (pkg_args.end (), eas.begin (), eas.end ()); - pkg_args.insert (pkg_args.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), eas.begin (), eas.end ()); + pkgs.insert (pkgs.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), pas.begin (), pas.end ()); - pkg_args.push_back ("config." + - t.name.variable () + - ".develop=false"); + pkgs.push_back ("config." + t.name.variable () + ".develop=false"); - pkg_args.push_back ("}+"); + pkgs.push_back ("}+"); // Strip the build-time mark. // - pkg_args.push_back (t.dependency::string ()); + pkgs.push_back (t.dependency::string ()); } #else if (has_buildtime_tests) { - pkg_args.push_back ("{"); + pkgs.push_back ("{"); - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (target_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (target_uuid); - pkg_args.push_back ("--checkout-root"); - pkg_args.push_back (dist_root.string ()); + pkgs.push_back ("--checkout-root"); + pkgs.push_back (dist_root.string ()); if (bootstrap_import) - pkg_args.push_back (*bootstrap_import); + pkgs.push_back (*bootstrap_import); - pkg_args.insert (pkg_args.end (), eas.begin (), eas.end ()); - pkg_args.insert (pkg_args.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), eas.begin (), eas.end ()); + pkgs.insert (pkgs.end (), cas.begin (), cas.end ()); + pkgs.insert (pkgs.end (), pas.begin (), pas.end ()); - pkg_args.push_back ("}+"); + pkgs.push_back ("}+"); // Add test dependency package constraints and group them if there // are multiple of them. // if (buildtime_tests.size () != 1) - pkg_args.push_back ("{"); + pkgs.push_back ("{"); // Strip the build-time mark. // for (const auto& t: buildtime_tests) - pkg_args.push_back (t.dependency::string ()); + pkgs.push_back (t.dependency::string ()); if (buildtime_tests.size () != 1) - pkg_args.push_back ("}"); + pkgs.push_back ("}"); } #endif } @@ -2414,21 +2619,22 @@ build (size_t argc, const char* argv[]) { if (!d.second.empty ()) { - pkg_args.push_back ("{"); + pkgs.push_back ("{"); - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (conf_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (conf_uuid); if (create_install) { - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (install_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (install_uuid); } - pkg_args.insert (pkg_args.end (), d.second.begin (), d.second.end ()); - pkg_args.push_back ("}+"); + pkgs.insert (pkgs.end (), d.second.begin (), d.second.end ()); + + pkgs.push_back ("}+"); - pkg_args.push_back (d.first); + pkgs.push_back (d.first); } else ++no_vars; @@ -2438,30 +2644,30 @@ build (size_t argc, const char* argv[]) // if (no_vars != 0) { - pkg_args.push_back ("{"); + pkgs.push_back ("{"); - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (conf_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (conf_uuid); if (create_install) { - pkg_args.push_back ("--config-uuid"); - pkg_args.push_back (install_uuid); + pkgs.push_back ("--config-uuid"); + pkgs.push_back (install_uuid); } - pkg_args.push_back ("}+"); + pkgs.push_back ("}+"); if (no_vars != 1) - pkg_args.push_back ("{"); + pkgs.push_back ("{"); for (const pair<string, strings>& d: pkg_config_deps) { if (d.second.empty ()) - pkg_args.push_back (d.first); + pkgs.push_back (d.first); } if (no_vars != 1) - pkg_args.push_back ("}"); + pkgs.push_back ("}"); } } } @@ -2481,16 +2687,17 @@ build (size_t argc, const char* argv[]) "-v", "build", "--configure-only", - pkg_config_opts, "--rebuild-checksum", tm.dependency_checksum ? *tm.dependency_checksum : "", "--yes", "-d", root_conf, step_args (env_args, s), step_args (tgt_args, s), + step_args (pkg_args, s), common_args, + pkg_config_opts, "--", - pkg_args); + pkgs); // The dependency checksum is tricky, here are the possibilities: // @@ -2585,952 +2792,1182 @@ build (size_t argc, const char* argv[]) Sleep (5000); #endif - // Update the main package. - // - { - operation_result& r (add_result ("update")); - - change_wd (trace, &r.log, rwd / main_pkg_conf); - - // bpkg update <env-config-args> <tgt-config-args> <package-name> - // - step_id b (step_id::bpkg_update); - step_id s (step_id::bpkg_update); - - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "update", - step_args (env_args, s), - step_args (tgt_args, s), - pkg); - - if (!r.status) - break; - - rm.status |= r.status; - } - - // Re-distribute if comes from a version control-based repository, update, - // and test external test packages in the bpkg configuration in the - // current working directory. Optionally pass the config.import.* variable - // override and/or set the environment variables for the bpkg processes. - // Return true if all operations for all packages succeeded. - // - // Pass true as the installed argument to use the test separate installed - // phase step ids (bpkg.test-separate-installed.*) and the test separate - // phase step ids (bpkg.test-separate.*) otherwise. In both cases fall - // back to the main phase step ids (bpkg.*) when no environment/ - // configuration arguments are specified for them. - // - auto test = [&trace, &wre, - &bkp_step, &bkp_status, &last_cmd, - &step_args, &tgt_args, &env_args, - &bootstrap_import, - &redist] - (operation_result& r, - const small_vector<test_dependency, 1>& tests, - const dir_path& dist_root, - bool installed, - const small_vector<string, 1>& envvars = {}) + auto fail_unreached_breakpoint = [&bkp_step, &fail_operation] + (operation_result& r) { - const optional<string>& import (!installed - ? bootstrap_import - : nullopt); - - for (const test_dependency& td: tests) - { - const string& pkg (td.name.string ()); - - // Re-distribute. - // - if (exists (dist_root)) - { - // Note that re-distributing the test package is a bit tricky since - // we don't know its version and so cannot deduce its source - // directory name easily. We could potentially run the bpkg-status - // command after the package is configured and parse the output to - // obtain the version. Let's, however, keep it simple and find the - // source directory using the package directory name pattern. - // - try - { - dir_path pkg_dir; - - // Note: doesn't follow symlinks. - // - path_search (dir_path (pkg + "-*/"), - [&pkg_dir] (path&& pe, const string&, bool interm) - { - if (!interm) - pkg_dir = path_cast<dir_path> (move (pe)); - - return interm; - }, - dist_root, - path_match_flags::none); - - if (!pkg_dir.empty ()) - { - step_id b ( - installed - ? step_id::bpkg_test_separate_installed_configure_build - : step_id::bpkg_configure_build); - - if (!redist (b, r, dist_root, pkg_dir, import, envvars)) - return false; - } - } - catch (const system_error& e) - { - fail << "unable to scan directory " << dist_root << ": " << e; - } - } + assert (bkp_step); - // Update. - // - // bpkg update <env-config-args> <tgt-config-args> <package-name> - // - { - step_id b (installed - ? step_id::bpkg_test_separate_installed_update - : step_id::bpkg_test_separate_update); - - step_id s (b); - - step_id f (step_id::bpkg_update); - - r.status |= run_bpkg ( - b, - envvars, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "update", - step_args (env_args, s, f), - step_args (tgt_args, s, f), - import, - pkg); - - if (!r.status) - return false; - } - - // Test. - // - // Note that we assume that the package supports the test operation - // since this is its main purpose. - // - // bpkg test <env-config-args> <tgt-config-args> <package-name> - // - { - step_id b (installed - ? step_id::bpkg_test_separate_installed_test - : step_id::bpkg_test_separate_test); - - step_id s (b); - - step_id f (step_id::bpkg_test); - - r.status |= run_bpkg ( - b, - envvars, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "test", - "--package-cwd", // See above for details. - step_args (env_args, s, f), - step_args (tgt_args, s, f), - import, - pkg); - - if (!r.status) - return false; - } - } - - return true; + fail_operation (r, + "interactive build breakpoint " + + to_string (*bkp_step) + " cannot be reached", + result_status::abort); }; - // Test the main package. - // - b_project_info prj ( - prj_info (pkg_dir, - b_info_flags::ext_mods | b_info_flags::subprojects, - "project")); - - // Run the internal tests if the test operation is supported by the - // project but only for the target package or if the configuration is - // self-hosted. + // Note that if the bpkg.update step is disabled, we also skip all the + // test and install related steps. // - bool has_internal_tests ((target_pkg || selfhost) && - find (prj.operations.begin (), - prj.operations.end (), - "test") != prj.operations.end ()); - - if (has_internal_tests || has_runtime_tests || has_buildtime_tests) + if (!step_disabled (step_id::bpkg_update)) { - operation_result& r (add_result ("test")); - - // Run internal tests. + // Update the main package. // - if (has_internal_tests) { - // Use --package-cwd to help ported to build2 third-party packages a - // bit (see bpkg-pkg-test(1) for details). - // - // Note that internal tests that load the module itself don't make - // much sense, thus we don't pass the config.import.* variable on - // the command line for modules that require bootstrap. - // - // bpkg test <env-config-args> <tgt-config-args> <package-name> + operation_result& r (add_result ("update")); + + change_wd (trace, &r.log, rwd / main_pkg_conf); + + // bpkg update <env-config-args> <tgt-config-args> <pkg-config-args> + // <package-name> // - step_id b (step_id::bpkg_test); - step_id s (step_id::bpkg_test); + step_id b (step_id::bpkg_update); + step_id s (step_id::bpkg_update); r.status |= run_bpkg ( b, trace, r.log, wre, bkp_step, bkp_status, last_cmd, "-v", - "test", - "--package-cwd", + "update", step_args (env_args, s), step_args (tgt_args, s), + step_args (pkg_args, s), pkg); if (!r.status) break; + + rm.status |= r.status; } - // External runtime tests. + // Re-distribute if comes from a version control-based repository, + // update, and test external test packages in the bpkg configuration in + // the current working directory. Optionally pass the config.import.* + // variable override and/or set the environment variables for the bpkg + // processes. Return true if all operations for all packages succeeded. // - // Note that we assume that these packages belong to the dependent - // package's repository or its complement repositories, recursively. - // Thus, we test them in the configuration used to build the dependent - // package. + // Pass true as the installed argument to use the test separate installed + // phase step ids (bpkg.test-separate-installed.*) and the test separate + // phase step ids (bpkg.test-separate.*) otherwise. In both cases fall + // back to the main phase step ids (bpkg.*) when no environment/ + // configuration arguments are specified for them. // - if (has_runtime_tests) - { - if (!test (r, runtime_tests, dist_root, false /* installed */)) - break; - } + auto test = [&trace, &wre, + &bkp_step, &bkp_status, &last_cmd, + &step_args, &env_args, &tgt_args, &pkg_args, + &bootstrap_import, + &redist] + (operation_result& r, + const small_vector<test_dependency, 1>& tests, + const dir_path& dist_root, + bool installed, + bool update_only, + const small_vector<string, 1>& envvars = {}) + { + const optional<string>& import (!installed + ? bootstrap_import + : nullopt); - // External build-time tests. - // - if (has_buildtime_tests) - { - change_wd (trace, &r.log, rwd / target_conf); + for (const test_dependency& td: tests) + { + const string& pkg (td.name.string ()); - if (!test (r, buildtime_tests, dist_root, false /* installed */)) - break; - } + // Re-distribute. + // + if (exists (dist_root)) + { + // Note that re-distributing the test package is a bit tricky + // since we don't know its version and so cannot deduce its + // source directory name easily. We could potentially run the + // bpkg-status command after the package is configured and parse + // the output to obtain the version. Let's, however, keep it + // simple and find the source directory using the package + // directory name pattern. + // + try + { + dir_path pkg_dir; - rm.status |= r.status; - } + // Note: doesn't follow symlinks. + // + path_search (dir_path (pkg + "-*/"), + [&pkg_dir] (path&& pe, const string&, bool interm) + { + if (!interm) + pkg_dir = path_cast<dir_path> (move (pe)); + + return interm; + }, + dist_root, + path_match_flags::none); + + if (!pkg_dir.empty ()) + { + step_id b ( + installed + ? step_id::bpkg_test_separate_installed_configure_build + : step_id::bpkg_configure_build); - // Install the package, optionally test the installation and uninstall - // afterwards. - // - if (!install_root) - break; + if (!redist (b, r, dist_root, pkg_dir, import, envvars)) + return false; + } + } + catch (const system_error& e) + { + fail << "unable to scan directory " << dist_root << ": " << e; + } + } - // Now the overall plan is as follows: - // - // 1. Install the package. - // - // 2. If the package has subprojects that support the test operation, then - // configure, build, and test them out of the source tree against the - // installed package using the build system directly. - // - // 3. If any of the test packages are specified, then configure, build, - // and test them in a separate bpkg configuration(s) against the - // installed package. - // - // 4. Uninstall the package. + // Update. + // + // bpkg update <env-config-args> <tgt-config-args> <pkg-config-args> + // <package-name> + // + { + step_id b (installed + ? step_id::bpkg_test_separate_installed_update + : step_id::bpkg_test_separate_update); - install_conf = rwd / (create_install ? install_conf : main_pkg_conf); + step_id s (b); - // Install. - // - { - operation_result& r (add_result ("install")); + step_id f (step_id::bpkg_update); - change_wd (trace, &r.log, install_conf); + r.status |= run_bpkg ( + b, + envvars, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "update", + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f), + import, + pkg); - // Note that for a host or module package we don't need the target - // configuration anymore, if present. So let's free up the space a - // little bit. - // - if (!target_pkg && create_target) - rm_r (trace, &r.log, rwd / target_conf); + if (!r.status) + return false; + } - // bpkg install <env-config-args> <tgt-config-args> <package-name> - // - step_id b (step_id::bpkg_install); - step_id s (step_id::bpkg_install); + // Test. + // + // Note that we assume that the package supports the test operation + // since this is its main purpose. + // + // bpkg test <env-config-args> <tgt-config-args> <pkg-config-args> + // <package-name> + // + if (!update_only) + { + step_id b (installed + ? step_id::bpkg_test_separate_installed_test + : step_id::bpkg_test_separate_test); - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "install", - step_args (env_args, s), - step_args (tgt_args, s), - pkg); + step_id s (b); - if (!r.status) - break; + step_id f (step_id::bpkg_test); - rm.status |= r.status; - } + r.status |= run_bpkg ( + b, + envvars, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "test", + "--package-cwd", // See above for details. + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f), + import, + pkg); - // Run the internal tests if the project contains "testable" subprojects, - // but not for a module. - // - has_internal_tests = false; + if (!r.status) + return false; + } + } - dir_paths subprj_dirs; // "Testable" package subprojects. + return true; + }; - if (!module_pkg) - { - // Collect the "testable" subprojects. + // Test the main package. // - for (const b_project_info::subproject& sp: prj.subprojects) - { - // Retrieve the subproject information similar to how we've done it - // for the package. - // - b_project_info si (prj_info (pkg_dir / sp.path, - b_info_flags::ext_mods, - "subproject")); - - const strings& ops (si.operations); - if (find (ops.begin (), ops.end (), "test") != ops.end ()) - subprj_dirs.push_back (sp.path); - } - - has_internal_tests = !subprj_dirs.empty (); - } - - if (has_internal_tests || has_runtime_tests || has_buildtime_tests) - { - operation_result& r (add_result ("test-installed")); - - change_wd (trace, &r.log, rwd); - - // Make sure that the installed package executables are properly - // imported when configuring and running tests, unless we are testing - // the build system module (that supposedly doesn't install any - // executables). + b_project_info prj ( + prj_info (pkg_dir, + b_info_flags::ext_mods | b_info_flags::subprojects, + "project")); + + // Run the internal tests if the test operation is supported by the + // project but only for the target package or if the configuration is + // self-hosted. // - small_vector<string, 1> envvars; + bool has_internal_tests ((target_pkg || selfhost) && + find (prj.operations.begin (), + prj.operations.end (), + "test") != prj.operations.end ()); - if (!module_pkg) + if (has_internal_tests || has_runtime_tests || has_buildtime_tests) { - // Note that we add the $config.install.root/bin directory at the - // beginning of the PATH environment variable value, so the installed - // executables are found first. - // - string paths ("PATH=" + (*install_root / "bin").string ()); + operation_result& r (add_result ("test")); - if (optional<string> s = getenv ("PATH")) - { - paths += path::traits_type::path_separator; - paths += *s; - } - - envvars.push_back (move (paths)); - } - - // Run internal tests. - // - if (has_internal_tests) - { - // Create the configuration. - // - // b create(<dir>, <env-modules>) <env-config-args> <tgt-config-args> + // Run internal tests. // - // Amalgamation directory that will contain configuration subdirectory - // for package tests out of source tree build. - // - dir_path out_dir ("build-installed"); - + if (has_internal_tests && !step_disabled (step_id::bpkg_test)) { - step_id b (step_id::b_test_installed_create); - step_id s (step_id::b_test_installed_create); - step_id f (step_id::b_create); - - string mods; // build2 create meta-operation parameters. - - for (const char* m: step_args (modules, s, f)) - { - mods += mods.empty () ? ", " : " "; - mods += m; - } + // Use --package-cwd to help ported to build2 third-party packages a + // bit (see bpkg-pkg-test(1) for details). + // + // Note that internal tests that load the module itself don't make + // much sense, thus we don't pass the config.import.* variable on + // the command line for modules that require bootstrap. + // + // bpkg test <env-config-args> <tgt-config-args> <pkg-config-args> + // <package-name> + // + step_id b (step_id::bpkg_test); + step_id s (step_id::bpkg_test); - r.status |= run_b ( + r.status |= run_bpkg ( b, trace, r.log, wre, bkp_step, bkp_status, last_cmd, - "-V", - "create('" + out_dir.representation () + '\'' + mods + ')', - step_args (env_args, s, f), - step_args (tgt_args, s, f)); + "-v", + "test", + "--package-cwd", + step_args (env_args, s), + step_args (tgt_args, s), + step_args (pkg_args, s), + pkg); if (!r.status) break; } - - // Configure testable subprojects sequentially and test/build in - // parallel afterwards. // - // It feels right to configure internal tests also passing the main - // package configuration variables, since they may need to align with - // the main package setup (enable some testscripts, etc). + // Fail if the breakpoint refers to the bpkg.test step but the package + // has no internal tests or this step is disabled. // - strings test_specs; - for (const dir_path& d: subprj_dirs) + else if (bkp_step && *bkp_step == step_id::bpkg_test) { - // b configure(<subprj-src-dir>@<subprj-out-dir>) <env-config-args> - // <tgt-config-args> - // <pkg-vars> - // - step_id b (step_id::b_test_installed_configure); - step_id s (step_id::b_test_installed_configure); - step_id f (step_id::b_configure); - - dir_path subprj_src_dir (exists (dist_src) - ? dist_src / d - : main_pkg_conf / pkg_dir / d); - - dir_path subprj_out_dir (out_dir / d); + fail_unreached_breakpoint (r); + break; + } - r.status |= run_b ( - b, - envvars, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "configure('" + - subprj_src_dir.representation () + "'@'" + - subprj_out_dir.representation () + "')", - step_args (env_args, s, f), - step_args (tgt_args, s, f), - pkg_config_vars); + // External tests. + // + // Note that if the bpkg.test-separate.update step is disabled, we + // also skip bpkg.test-separate.test. + // + if ((has_runtime_tests || has_buildtime_tests) && + !step_disabled (step_id::bpkg_test_separate_update)) + { + bool update_only (step_disabled (step_id::bpkg_test_separate_test)); - if (!r.status) + // Fail if the breakpoint refers to the bpkg.test-separate.test step + // but this step is disabled. + // + if (update_only && + bkp_step && + *bkp_step == step_id::bpkg_test_separate_test) + { + fail_unreached_breakpoint (r); break; + } - test_specs.push_back ( - "test('" + subprj_out_dir.representation () + "')"); - } + // External runtime tests. + // + // Note that we assume that these packages belong to the dependent + // package's repository or its complement repositories, recursively. + // Thus, we test them in the configuration used to build the + // dependent package. + // + if (has_runtime_tests) + { + if (!test (r, + runtime_tests, + dist_root, + false /* installed */, + update_only)) + break; + } - if (!r.status) - break; + // External build-time tests. + // + if (has_buildtime_tests) + { + change_wd (trace, &r.log, rwd / target_conf); - // Build/test subprojects. + if (!test (r, + buildtime_tests, + dist_root, + false /* installed */, + update_only)) + break; + } + } // - // b test(<subprj-out-dir>)... <env-config-args> <tgt-config-args> + // Fail if the breakpoint refers to some of the bpkg.test-separate.* + // steps but the package either has no external tests or the + // bpkg.test-separate.update step is disabled. // + else if (bkp_step && + *bkp_step >= step_id::bpkg_test_separate_update && + *bkp_step <= step_id::bpkg_test_separate_test) { - step_id b (step_id::b_test_installed_test); - step_id s (step_id::b_test_installed_test); - - r.status |= run_b ( - b, - envvars, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - test_specs, - step_args (env_args, s), - step_args (tgt_args, s)); - - if (!r.status) - break; + fail_unreached_breakpoint (r); + break; } - } - // Run runtime and build-time tests. + rm.status |= r.status; + } // - // Note that we only build runtime tests for target packages and for - // host packages in self-hosted configurations. + // Fail if the breakpoint refers to some of the test steps but the + // package has no tests. // - if (has_runtime_tests || has_buildtime_tests) + else if (bkp_step && + *bkp_step >= step_id::bpkg_test && + *bkp_step <= step_id::bpkg_test_separate_test) { - // Create the required build configurations. - // - dir_path target_conf ("build-installed-bpkg"); - dir_path host_conf ("build-installed-bpkg-host"); - dir_path module_conf ("build-installed-bpkg-module"); + fail_unreached_breakpoint (add_result ("test")); + break; + } - // Create the target configuration if this is a target package having - // external runtime tests or a host/module package having external - // build-time tests. + // Install the package, optionally test the installation and uninstall + // afterwards. + // + // Note that if the bpkg.install step is disabled, we also skip all the + // test installed related steps up to bpkg.uninstall. + // + if (install_root && !step_disabled (step_id::bpkg_install)) + { + // Now the overall plan is as follows: // - bool create_target (target_pkg || has_buildtime_tests); - - // Note that even if there are no runtime tests for a host/module - // package, we still need to create the host/build2 configuration to - // configure the system package in. + // 1. Install the package. // - bool create_host (host_pkg || module_pkg); - bool create_module (module_pkg || (host_pkg && has_buildtime_tests)); - - // Note: a module package cannot have runtime tests and so the module - // configuration is only created to serve build-time tests. Thus, the - // host or target configuration is always created as well and the - // module configuration is never a root configuration. + // 2. If the package has subprojects that support the test operation, + // then configure, build, and test them out of the source tree + // against the installed package using the build system directly. // - assert (create_target || create_host); - - // Root configuration through which we will be configuring the - // cluster. + // 3. If any of the test packages are specified, then configure, build, + // and test them in a separate bpkg configuration(s) against the + // installed package. // - const dir_path& root_conf (create_target ? target_conf : host_conf); + // 4. Uninstall the package. - // Runtime tests configuration. Should only be used if there are any. - // - const dir_path& runtime_tests_conf (target_pkg - ? target_conf - : host_conf); + install_conf = rwd / (create_install ? install_conf : main_pkg_conf); - // Create the target configuration. - // - // bpkg create <env-modules> <env-config-args> <tgt-config-args> + // Install. // - if (create_target) { - step_id b (step_id::bpkg_test_separate_installed_create); + operation_result& r (add_result ("install")); - // Note that here and below the _for_* step ids are determined by - // the main package type (and, yes, that means we will use the same - // step ids for target and host configuration -- that, however, - // should be ok since host configuration will only be created in - // the self-hosted case). + change_wd (trace, &r.log, install_conf); + + // Note that for a host or module package we don't need the target + // configuration anymore, if present. So let's free up the space a + // little bit. // - step_id s ( - target_pkg - ? step_id::bpkg_test_separate_installed_create_for_target - : host_pkg - ? step_id::bpkg_test_separate_installed_create_for_host - : step_id::bpkg_test_separate_installed_create_for_module); - - // Note: no fallback for modules. + if (!target_pkg && create_target) + rm_r (trace, &r.log, rwd / target_conf); + + // bpkg install <env-config-args> <tgt-config-args> <pkg-config-args> + // <package-name> // - optional<step_id> f (!module_pkg - ? step_id::bpkg_test_separate_installed_create - : optional<step_id> ()); + step_id b (step_id::bpkg_install); + step_id s (step_id::bpkg_install); r.status |= run_bpkg ( b, trace, r.log, wre, bkp_step, bkp_status, last_cmd, - "-V", - "create", - "-d", target_conf, - step_args (modules, s, f), - step_args (env_args, s, f), - step_args (tgt_args, s, f)); + "-v", + "install", + step_args (env_args, s), + step_args (tgt_args, s), + step_args (pkg_args, s), + pkg); if (!r.status) break; + + rm.status |= r.status; } - // Create the host configuration. + // Run the internal tests if the project contains "testable" + // subprojects, but not for a module. // - if (create_host) - { - // bpkg create --type host <env-modules> <env-config-args> <tgt-config-args> - // - step_id b (step_id::bpkg_test_separate_installed_create); + has_internal_tests = false; - step_id s ( - host_pkg - ? step_id::bpkg_test_separate_installed_create_for_host - : step_id::bpkg_test_separate_installed_create_for_module); + dir_paths subprj_dirs; // "Testable" package subprojects. - // Note: no fallback for modules. + if (!module_pkg) + { + // Collect the "testable" subprojects. // - optional<step_id> f (!module_pkg - ? step_id::bpkg_test_separate_installed_create - : optional<step_id> ()); + for (const b_project_info::subproject& sp: prj.subprojects) + { + // Retrieve the subproject information similar to how we've done it + // for the package. + // + b_project_info si (prj_info (pkg_dir / sp.path, + b_info_flags::ext_mods, + "subproject")); - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-V", - "create", - "-d", host_conf, - "--type", "host", - "--name", "host", - step_args (modules, s, f), - step_args (env_args, s, f), - step_args (tgt_args, s, f)); + const strings& ops (si.operations); + if (find (ops.begin (), ops.end (), "test") != ops.end ()) + subprj_dirs.push_back (sp.path); + } - if (!r.status) - break; + has_internal_tests = !subprj_dirs.empty (); } - // Create the module configuration. - // - // Note that we never build any tests in it but only configure the - // system package. Note, however, that the host/module package - // build-time tests can potentially build some other modules here. - // - if (create_module) + if (has_internal_tests || has_runtime_tests || has_buildtime_tests) { - // b create(<dir>) config.config.load=~build2 + operation_result& r (add_result ("test-installed")); + + change_wd (trace, &r.log, rwd); + + // Make sure that the installed package executables are properly + // imported when configuring and running tests, unless we are testing + // the build system module (that supposedly doesn't install any + // executables). // - step_id b (step_id::bpkg_test_separate_installed_create); + small_vector<string, 1> envvars; - r.status |= run_b ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-V", - "create(" + module_conf.representation () + ",cc)", - "config.config.load=~build2", - "config.config.persist+='config.*'@unused=drop"); + if (!module_pkg) + { + // Note that we add the $config.install.root/bin directory at the + // beginning of the PATH environment variable value, so the + // installed executables are found first. + // + string paths ("PATH=" + (*install_root / "bin").string ()); - if (!r.status) - break; + if (optional<string> s = getenv ("PATH")) + { + paths += path::traits_type::path_separator; + paths += *s; + } - // bpkg create --existing --type build2 + envvars.push_back (move (paths)); + } + + // Run internal tests. // - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "create", - "--existing", - "-d", module_conf, - "--type", "build2", - "--name", "module"); + if (has_internal_tests) + { + // Create the configuration. + // + // b create(<dir>, <env-modules>) <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // + // Amalgamation directory that will contain configuration + // subdirectory for package tests out of source tree build. + // + dir_path out_dir ("build-installed"); - if (!r.status) - break; - } + { + step_id b (step_id::b_test_installed_create); + step_id s (step_id::b_test_installed_create); + step_id f (step_id::b_create); - // Link the configurations. - // - // bpkg link -d <dir> <dir> - // - { - step_id b (step_id::bpkg_test_separate_installed_link); + string mods; // build2 create meta-operation parameters. - if (create_target) - { - if (create_host) + for (const char* m: step_args (modules, s, f)) + { + mods += mods.empty () ? ", " : " "; + mods += m; + } + + r.status |= run_b ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-V", + "create('" + out_dir.representation () + '\'' + mods + ')', + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f)); + + if (!r.status) + break; + } + + // Configure testable subprojects sequentially and test/build in + // parallel afterwards. + // + // It feels right to configure internal tests also passing the + // main package configuration variables, since they may need to + // align with the main package setup (enable some testscripts, + // etc). + // + strings test_specs; + for (const dir_path& d: subprj_dirs) { - r.status |= run_bpkg ( + // b configure(<subprj-src-dir>@<subprj-out-dir>) <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // <pkg-vars> + // + step_id b (step_id::b_test_installed_configure); + step_id s (step_id::b_test_installed_configure); + step_id f (step_id::b_configure); + + dir_path subprj_src_dir (exists (dist_src) + ? dist_src / d + : main_pkg_conf / pkg_dir / d); + + dir_path subprj_out_dir (out_dir / d); + + r.status |= run_b ( b, + envvars, trace, r.log, wre, bkp_step, bkp_status, last_cmd, "-v", - "link", - "-d", target_conf, - host_conf); + "configure('" + + subprj_src_dir.representation () + "'@'" + + subprj_out_dir.representation () + "')", + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f), + pkg_config_vars); if (!r.status) break; + + test_specs.push_back ( + "test('" + subprj_out_dir.representation () + "')"); } - if (create_module) + if (!r.status) + break; + + // Build/test subprojects. + // + // b test(<subprj-out-dir>)... <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // + if (!step_disabled (step_id::b_test_installed_test)) { - r.status |= run_bpkg ( + step_id b (step_id::b_test_installed_test); + step_id s (step_id::b_test_installed_test); + + r.status |= run_b ( b, + envvars, trace, r.log, wre, bkp_step, bkp_status, last_cmd, "-v", - "link", - "-d", target_conf, - module_conf); + test_specs, + step_args (env_args, s), + step_args (tgt_args, s), + step_args (pkg_args, s)); if (!r.status) break; } + // + // Fail if the breakpoint refers to the b.test-installed.test step + // but this step is disabled. + // + else if (bkp_step && *bkp_step == step_id::b_test_installed_test) + { + fail_unreached_breakpoint (r); + break; + } + } + // + // Fail if the breakpoint refers to some of the b.test-installed.* + // steps but the package doesn't have any internal tests. + // + else if (bkp_step && + *bkp_step >= step_id::b_test_installed_create && + *bkp_step <= step_id::b_test_installed_test) + { + fail_unreached_breakpoint (r); + break; } - if (create_host) + // Run runtime and build-time tests. + // + // Note that we only build runtime tests for target packages and for + // host packages in self-hosted configurations. + // + if (has_runtime_tests || has_buildtime_tests) { + // Create the required build configurations. + // + dir_path target_conf ("build-installed-bpkg"); + dir_path host_conf ("build-installed-bpkg-host"); + dir_path module_conf ("build-installed-bpkg-module"); + + // Create the target configuration if this is a target package + // having external runtime tests or a host/module package having + // external build-time tests. + // + bool create_target (target_pkg || has_buildtime_tests); + + // Note that even if there are no runtime tests for a host/module + // package, we still need to create the host/build2 configuration + // to configure the system package in. + // + bool create_host (host_pkg || module_pkg); + + bool create_module (module_pkg || + (host_pkg && has_buildtime_tests)); + + // Note: a module package cannot have runtime tests and so the + // module configuration is only created to serve build-time tests. + // Thus, the host or target configuration is always created as + // well and the module configuration is never a root + // configuration. + // + assert (create_target || create_host); + + // Root configuration through which we will be configuring the + // cluster. + // + const dir_path& root_conf (create_target ? target_conf : host_conf); + + // Runtime tests configuration. Should only be used if there are + // any. + // + const dir_path& runtime_tests_conf (target_pkg + ? target_conf + : host_conf); + + // Create the target configuration. + // + // bpkg create <env-modules> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // + if (create_target) + { + step_id b (step_id::bpkg_test_separate_installed_create); + + // Note that here and below the _for_* step ids are determined + // by the main package type (and, yes, that means we will use + // the same step ids for target and host configuration -- that, + // however, should be ok since host configuration will only be + // created in the self-hosted case). + // + step_id s ( + target_pkg + ? step_id::bpkg_test_separate_installed_create_for_target + : host_pkg + ? step_id::bpkg_test_separate_installed_create_for_host + : step_id::bpkg_test_separate_installed_create_for_module); + + // Note: no fallback for modules. + // + optional<step_id> f (!module_pkg + ? step_id::bpkg_test_separate_installed_create + : optional<step_id> ()); + + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-V", + "create", + "-d", target_conf, + step_args (modules, s, f), + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f)); + + if (!r.status) + break; + } + + // Create the host configuration. + // + if (create_host) + { + // bpkg create --type host <env-modules> <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // + step_id b (step_id::bpkg_test_separate_installed_create); + + step_id s (host_pkg + ? step_id::bpkg_test_separate_installed_create_for_host + : step_id::bpkg_test_separate_installed_create_for_module); + + // Note: no fallback for modules. + // + optional<step_id> f (!module_pkg + ? step_id::bpkg_test_separate_installed_create + : optional<step_id> ()); + + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-V", + "create", + "-d", host_conf, + "--type", "host", + "--name", "host", + step_args (modules, s, f), + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f)); + + if (!r.status) + break; + } + + // Create the module configuration. + // + // Note that we never build any tests in it but only configure the + // system package. Note, however, that the host/module package + // build-time tests can potentially build some other modules here. + // if (create_module) { + // b create(<dir>) config.config.load=~build2 + // + step_id b (step_id::bpkg_test_separate_installed_create); + + r.status |= run_b ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-V", + "create(" + module_conf.representation () + ",cc)", + "config.config.load=~build2", + "config.config.persist+='config.*'@unused=drop"); + + if (!r.status) + break; + + // bpkg create --existing --type build2 + // r.status |= run_bpkg ( b, trace, r.log, wre, bkp_step, bkp_status, last_cmd, "-v", - "link", - "-d", host_conf, - module_conf); + "create", + "--existing", + "-d", module_conf, + "--type", "build2", + "--name", "module"); if (!r.status) break; } - } - } - // Add and fetch the repositories. - // - if (has_runtime_tests) - { - // bpkg add <env-config-args> <tgt-config-args> <repository-url> - // - { - step_id b (step_id::bpkg_test_separate_installed_configure_add); - step_id s (step_id::bpkg_test_separate_installed_configure_add); - step_id f (step_id::bpkg_configure_add); + // Link the configurations. + // + // bpkg link -d <dir> <dir> + // + { + step_id b (step_id::bpkg_test_separate_installed_link); - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "add", - "-d", runtime_tests_conf, - step_args (env_args, s, f), - step_args (tgt_args, s, f), - repo); + if (create_target) + { + if (create_host) + { + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "link", + "-d", target_conf, + host_conf); + + if (!r.status) + break; + } - if (!r.status) - break; - } + if (create_module) + { + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "link", + "-d", target_conf, + module_conf); + + if (!r.status) + break; + } + } - // bpkg fetch <env-config-args> <tgt-config-args> <trust-options> - // - { - step_id b (step_id::bpkg_test_separate_installed_configure_fetch); - step_id s (step_id::bpkg_test_separate_installed_configure_fetch); - step_id f (step_id::bpkg_configure_fetch); + if (create_host) + { + if (create_module) + { + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "link", + "-d", host_conf, + module_conf); + + if (!r.status) + break; + } + } + } - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "fetch", - "-d", runtime_tests_conf, - step_args (env_args, s, f), - step_args (tgt_args, s, f), - trust_ops); + // Add and fetch the repositories. + // + if (has_runtime_tests) + { + // bpkg add <env-config-args> <tgt-config-args> <pkg-config-args> + // <repository-url> + // + { + step_id b (step_id::bpkg_test_separate_installed_configure_add); + step_id s (step_id::bpkg_test_separate_installed_configure_add); + step_id f (step_id::bpkg_configure_add); - if (!r.status) - break; - } - } + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "add", + "-d", runtime_tests_conf, + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f), + repo); + + if (!r.status) + break; + } - if (has_buildtime_tests) - { - // bpkg add <env-config-args> <tgt-config-args> <repository-url> - // - { - step_id b (step_id::bpkg_test_separate_installed_configure_add); - step_id s (step_id::bpkg_test_separate_installed_configure_add); - step_id f (step_id::bpkg_configure_add); + // bpkg fetch <env-config-args> <tgt-config-args> <pkg-config-args> + // <trust-options> + // + { + step_id b (step_id::bpkg_test_separate_installed_configure_fetch); + step_id s (step_id::bpkg_test_separate_installed_configure_fetch); + step_id f (step_id::bpkg_configure_fetch); - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "add", - "-d", target_conf, - step_args (env_args, s, f), - step_args (tgt_args, s, f), - repo); + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "fetch", + "-d", runtime_tests_conf, + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f), + trust_ops); + + if (!r.status) + break; + } + } - if (!r.status) - break; - } + if (has_buildtime_tests) + { + // bpkg add <env-config-args> <tgt-config-args> <pkg-config-args> + // <repository-url> + // + { + step_id b (step_id::bpkg_test_separate_installed_configure_add); + step_id s (step_id::bpkg_test_separate_installed_configure_add); + step_id f (step_id::bpkg_configure_add); - // bpkg fetch <env-config-args> <tgt-config-args> <trust-options> - // - { - step_id b (step_id::bpkg_test_separate_installed_configure_fetch); - step_id s (step_id::bpkg_test_separate_installed_configure_fetch); - step_id f (step_id::bpkg_configure_fetch); + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "add", + "-d", target_conf, + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f), + repo); + + if (!r.status) + break; + } - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "fetch", - "-d", target_conf, - step_args (env_args, s, f), - step_args (tgt_args, s, f), - trust_ops); + // bpkg fetch <env-config-args> <tgt-config-args> <pkg-config-args> + // <trust-options> + // + { + step_id b (step_id::bpkg_test_separate_installed_configure_fetch); + step_id s (step_id::bpkg_test_separate_installed_configure_fetch); + step_id f (step_id::bpkg_configure_fetch); - if (!r.status) - break; - } - } + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "fetch", + "-d", target_conf, + step_args (env_args, s, f), + step_args (tgt_args, s, f), + step_args (pkg_args, s, f), + trust_ops); + + if (!r.status) + break; + } + } - // Configure all the packages using a single bpkg-pkg-build command. - // - // bpkg build --configure-only <env-config-args> <tgt-config-args> - // { <config> }+ { <runtime-test>... } - // <buildtime-test>... - // ?sys:<pkg> - // - strings pkg_args; + // Configure all the packages using a single bpkg-pkg-build command. + // + // bpkg build --configure-only <env-config-args> + // <tgt-config-args> + // <pkg-config-args> + // { <config> }+ { <runtime-test>... } + // <buildtime-test>... + // ?sys:<pkg> + // + strings pkgs; - if (has_runtime_tests) - { - // Note that only host package runtime tests can (but not - // necessarily) be configured in a linked configuration and require - // --config-name to be specified for them. - // - assert (!module_pkg); + if (has_runtime_tests) + { + // Note that only host package runtime tests can (but not + // necessarily) be configured in a linked configuration and + // require --config-name to be specified for them. + // + assert (!module_pkg); - string conf_name (runtime_tests_conf == root_conf - ? "" - : "host"); + string conf_name (runtime_tests_conf == root_conf + ? "" + : "host"); - bool og (!conf_name.empty ()); + bool og (!conf_name.empty ()); - if (og) - { - pkg_args.push_back ("{"); + if (og) + { + pkgs.push_back ("{"); - pkg_args.push_back ("--config-name"); - pkg_args.push_back (conf_name); + pkgs.push_back ("--config-name"); + pkgs.push_back (conf_name); - pkg_args.push_back ("}+"); - } + pkgs.push_back ("}+"); + } - if (og && runtime_tests.size () != 1) - pkg_args.push_back ("{"); + if (og && runtime_tests.size () != 1) + pkgs.push_back ("{"); - for (const auto& t: runtime_tests) - pkg_args.push_back (t.string ()); + for (const auto& t: runtime_tests) + pkgs.push_back (t.string ()); - if (og && runtime_tests.size () != 1) - pkg_args.push_back ("}"); - } + if (og && runtime_tests.size () != 1) + pkgs.push_back ("}"); + } - if (has_buildtime_tests) - { - // Strip the build-time mark. - // - for (const auto& t: buildtime_tests) - pkg_args.push_back (t.dependency::string ()); - } + if (has_buildtime_tests) + { + // Strip the build-time mark. + // + for (const auto& t: buildtime_tests) + pkgs.push_back (t.dependency::string ()); + } - pkg_args.push_back ("?sys:" + pkg_rev); + pkgs.push_back ("?sys:" + pkg_rev); - // Finally, configure all the test packages. - // - { - step_id b (step_id::bpkg_test_separate_installed_configure_build); + // Finally, configure all the test packages. + // + { + step_id b (step_id::bpkg_test_separate_installed_configure_build); - step_id g (step_id::bpkg_global_configure_build); // Global. + step_id g (step_id::bpkg_global_configure_build); // Global. - step_id s ( - target_pkg - ? step_id::bpkg_test_separate_installed_create_for_target - : host_pkg - ? step_id::bpkg_test_separate_installed_create_for_host - : step_id::bpkg_test_separate_installed_create_for_module); + step_id s ( + target_pkg + ? step_id::bpkg_test_separate_installed_create_for_target + : host_pkg + ? step_id::bpkg_test_separate_installed_create_for_host + : step_id::bpkg_test_separate_installed_create_for_module); - step_id f (step_id::bpkg_test_separate_installed_configure_build); + step_id f (step_id::bpkg_test_separate_installed_configure_build); - r.status |= run_bpkg ( - b, - envvars, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "build", - "--configure-only", - "--checkout-root", dist_installed_root, - "--yes", - "-d", root_conf, - step_args (env_args, g), - step_args (env_args, s, f), - step_args (tgt_args, g), - step_args (tgt_args, s, f), - "--", - pkg_args); + r.status |= run_bpkg ( + b, + envvars, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "build", + "--configure-only", + "--checkout-root", dist_installed_root, + "--yes", + "-d", root_conf, + step_args (env_args, g), + step_args (env_args, s, f), + step_args (tgt_args, g), + step_args (tgt_args, s, f), + step_args (pkg_args, g), + step_args (pkg_args, s, f), + "--", + pkgs); - if (!r.status) - break; - } + if (!r.status) + break; + } #ifdef _WIN32 - Sleep (5000); // See above. + Sleep (5000); // See above. #endif - // Run external runtime tests. - // - if (has_runtime_tests) - { - const dir_path& runtime_tests_conf (target_pkg - ? target_conf - : host_conf); + // Note that if bpkg.test-separate-installed.update step is + // disabled, we also skip bpkg.test-separate-installed.test. + // + if (!step_disabled (step_id::bpkg_test_separate_installed_update)) + { + bool update_only ( + step_disabled (step_id::bpkg_test_separate_installed_test)); + + // Fail if the breakpoint refers to the + // bpkg.test-separate-installed.test step but this step is + // disabled. + // + if (update_only && + bkp_step && + *bkp_step == step_id::bpkg_test_separate_installed_test) + { + fail_unreached_breakpoint (r); + break; + } + + // External runtime tests. + // + if (has_runtime_tests) + { + const dir_path& runtime_tests_conf (target_pkg + ? target_conf + : host_conf); + + change_wd (trace, &r.log, runtime_tests_conf); + + if (!test (r, + runtime_tests, + dist_installed_root, + true /* installed */, + update_only, + envvars)) + break; + } - change_wd (trace, &r.log, runtime_tests_conf); + // External build-time tests. + // + if (has_buildtime_tests) + { + change_wd (trace, &r.log, rwd / target_conf); + + if (!test (r, + buildtime_tests, + dist_installed_root, + true /* installed */, + update_only, + envvars)) + break; + } + } + // + // Fail if the breakpoint refers to some of the + // bpkg.test-separate-installed.{update,test} steps but the + // bpkg.test-separate-installed.update step is disabled. + // + else if (bkp_step && + *bkp_step >= step_id::bpkg_test_separate_installed_update && + *bkp_step <= step_id::bpkg_test_separate_installed_test) + { + fail_unreached_breakpoint (r); + break; + } - if (!test (r, - runtime_tests, - dist_installed_root, - true /* installed */, - envvars)) + rm.status |= r.status; + } + // + // Fail if the breakpoint refers to some of the + // bpkg.test-separate-installed.* steps but the package has no + // external tests. + // + else if (bkp_step && + *bkp_step >= step_id::bpkg_test_separate_installed_create && + *bkp_step <= step_id::bpkg_test_separate_installed_test) + { + fail_unreached_breakpoint (r); break; + } } - - // Run external build-time tests. // - if (has_buildtime_tests) + // Fail if the breakpoint refers to some of the test installed steps + // but the package has no tests. + // + else if (bkp_step && + *bkp_step >= step_id::b_test_installed_create && + *bkp_step <= step_id::bpkg_test_separate_installed_test) { - change_wd (trace, &r.log, rwd / target_conf); - - if (!test (r, - buildtime_tests, - dist_installed_root, - true /* installed */, - envvars)) + fail_unreached_breakpoint (add_result ("test-installed")); break; } - rm.status |= r.status; - } - } + // Uninstall. + // + { + operation_result& r (add_result ("uninstall")); - // Uninstall. - // - { - operation_result& r (add_result ("uninstall")); + change_wd (trace, &r.log, install_conf); - change_wd (trace, &r.log, install_conf); + // bpkg uninstall <env-config-args> <tgt-config-args> <pkg-config-args> + // <package-name> + // + step_id b (step_id::bpkg_uninstall); + step_id s (step_id::bpkg_uninstall); - // bpkg uninstall <env-config-args> <tgt-config-args> <package-name> - // - step_id b (step_id::bpkg_uninstall); - step_id s (step_id::bpkg_uninstall); + r.status |= run_bpkg ( + b, + trace, r.log, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "uninstall", + step_args (env_args, s), + step_args (tgt_args, s), + step_args (pkg_args, s), + pkg); - r.status |= run_bpkg ( - b, - trace, r.log, wre, - bkp_step, bkp_status, last_cmd, - "-v", - "uninstall", - step_args (env_args, s), - step_args (tgt_args, s), - pkg); + if (!r.status) + break; - if (!r.status) + rm.status |= r.status; + } + } + // + // Fail if the breakpoint refers to some of the install/test installed + // steps but the package either is not supposed to be installed (the + // target configuration doesn't specify config.install.root, etc) or the + // bpkg.install step is disabled. + // + else if (bkp_step && + *bkp_step >= step_id::bpkg_install && + *bkp_step <= step_id::bpkg_uninstall) + { + fail_unreached_breakpoint (add_result ("install")); break; - - rm.status |= r.status; + } + } + // + // Fail if the breakpoint refers to bpkg.update or any dependent step but + // the bpkg.update step is disabled. + // + else if (bkp_step && + *bkp_step >= step_id::bpkg_update && + *bkp_step <= step_id::bpkg_uninstall) + { + fail_unreached_breakpoint (add_result ("update")); + break; } break; |