From b92c1c200deef9a4e2bbd686080ecc4c64c22d10 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 4 Apr 2023 01:10:44 +0300 Subject: Add support for bpkg.bindist.* step ids --- bbot/worker/worker.cxx | 716 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 577 insertions(+), 139 deletions(-) (limited to 'bbot') diff --git a/bbot/worker/worker.cxx b/bbot/worker/worker.cxx index 3367aaa..b33fb9f 100644 --- a/bbot/worker/worker.cxx +++ b/bbot/worker/worker.cxx @@ -22,6 +22,8 @@ #include #include +#include + #include #include @@ -60,7 +62,7 @@ namespace bbot const size_t tftp_get_retries (3); // Task request retries (see startup()). } -bool +static bool exists (const dir_path& d) try { @@ -206,10 +208,25 @@ enum class step_id bpkg_test_separate_test, //: bpkg_test // Note that we only perform the installation tests if this is a target - // package or a self-hosted configuration. + // package or a self-hosted configuration. Also note that this step is + // considered disabled if any of the bpkg_bindist_* steps is explicitly + // enabled. // bpkg_install, + // Note that the bpkg_bindist_* steps are mutually exclusive and the latest + // status change for them (via the leading +/- characters in the prefix) + // overrides all the previous ones. + // + bpkg_bindist_debian, + bpkg_bindist_fedora, + bpkg_bindist_archive, + + // Note that this step is considered disabled unless one of the + // bpkg_bindist_* steps is explicitly enabled. + // + bbot_sys_install, + // Note: skipped for modules. // b_test_installed_create, //: b_create @@ -248,6 +265,8 @@ enum class step_id bpkg_uninstall, + bbot_sys_uninstall, + end }; @@ -280,6 +299,12 @@ static const strings step_id_str { "bpkg.install", + "bpkg.bindist.debian", + "bpkg.bindist.fedora", + "bpkg.bindist.archive", + + "bbot.sys-install", + "b.test-installed.create", "b.test-installed.configure", "b.test-installed.test", @@ -303,6 +328,8 @@ static const strings step_id_str { "bpkg.uninstall", + "bbot.sys-uninstall", + "end"}; static inline const string& @@ -315,14 +342,36 @@ using std::regex; namespace regex_constants = std::regex_constants; using regexes = vector; +// UTF-8-sanitize and log the line. Also print it to a tracer, if specified, +// or to stderr otherwise at verbosity level 3 or higher. +// +static void +log_line (string&& l, string& log, tracer* trace = nullptr) +{ + if (verb >= 3) + { + if (trace != nullptr) + *trace << l; + else + text << l; + } + + to_utf8 (l, '?', codepoint_types::graphic, U"\n\r\t"); + + log += l; + log += '\n'; +} + // Run the worker script command. Name is used for logging and diagnostics // only. Match lines read from the command's stderr against the regular // expressions and return the warning result status (instead of success) in // case of a match. Save the executed command into last_cmd. // -// Redirect stdout to stderr if the out argument is NULL. Otherwise, save the -// process output into the referenced variable. Note: currently assumes that -// the output will always fit into the pipe buffer. +// Redirect stdout to stderr if the out_* arguments are not specified (out_str +// is NULL and out_file is empty; must never be specified both). Otherwise, +// save the process output into the variable referenced by out_str, if +// specified, and to the file referenced by out_file otherwise. Note: in the +// former case assumes that the output will always fit into the pipe buffer. // // If bkp_step is present and is equal to the command step, then prior to // running this command ask the user if to continue or abort the task @@ -336,7 +385,9 @@ template static result_status run_cmd (step_id step, tracer& t, - string& log, optional* out, const regexes& warn_detect, + string& log, + optional* out_str, const path& out_file, + const regexes& warn_detect, const string& name, const optional& bkp_step, const optional& bkp_status, @@ -344,24 +395,7 @@ run_cmd (step_id step, const process_env& pe, A&&... a) { - // UTF-8-sanitize and log the diagnostics. Also print the raw diagnostics - // to stderr at verbosity level 3 or higher. - // - auto add = [&log, &t] (string&& s, bool trace = true) - { - if (verb >= 3) - { - if (trace) - t << s; - else - text << s; - } - - to_utf8 (s, '?', codepoint_types::graphic, U"\n\r\t"); - - log += s; - log += '\n'; - }; + assert (out_str == nullptr || out_file.empty ()); string next_cmd; @@ -370,7 +404,7 @@ run_cmd (step_id step, // struct abort {}; - auto prompt = [&last_cmd, &next_cmd, &add] (const string& what) + auto prompt = [&last_cmd, &next_cmd, &t, &log] (const string& what) { diag_record dr (text); @@ -392,7 +426,7 @@ run_cmd (step_id step, if (!yn_prompt ( "continue execution (or you may shutdown the machine)? [y/n]")) { - add ("execution aborted by interactive user"); + log_line ("execution aborted by interactive user", log, &t); throw abort (); } }; @@ -455,16 +489,30 @@ run_cmd (step_id step, // // Text mode seems appropriate. // - fdpipe out_pipe (out != nullptr ? fdopen_pipe () : fdpipe ()); + fdpipe out_pipe (out_str != nullptr ? fdopen_pipe () : fdpipe ()); fdpipe err_pipe (fdopen_pipe ()); + // If the output file is specified, then open "half-pipe". + // + if (!out_file.empty ()) + try + { + out_pipe.out = fdopen (out_file, + fdopen_mode::out | fdopen_mode::create); + } + catch (const io_error& e) + { + fail << "unable to open " << out_file << ": " << e; + } + process pr ( - process_start_callback (cmdc, - fdopen_null (), // Never reads from stdin. - out != nullptr ? out_pipe.out.get () : 2, - err_pipe, - pe, - forward (a)...)); + process_start_callback ( + cmdc, + fdopen_null (), // Never reads from stdin. + out_pipe.out != nullfd ? out_pipe.out.get () : 2, + err_pipe, + pe, + forward (a)...)); out_pipe.out.close (); err_pipe.out.close (); @@ -502,25 +550,25 @@ run_cmd (step_id step, } } - add (move (l), false /* trace */); + log_line (move (l), log); } } if (!pr.wait ()) { const process_exit& e (*pr.exit); - add (name + ' ' + to_string (e)); + log_line (name + ' ' + to_string (e), log, &t); r = e.normal () ? result_status::error : result_status::abnormal; } // Only read the buffered output if the process terminated normally. // - if (out != nullptr && pr.exit->normal ()) + if (out_str != nullptr && pr.exit->normal ()) { // Note: shouldn't throw since the output is buffered. // ifdstream is (move (out_pipe.in)); - *out = is.read_text (); + *out_str = is.read_text (); } last_cmd = move (next_cmd); @@ -559,7 +607,7 @@ static result_status run_bpkg (step_id step, const V& envvars, tracer& t, - string& log, optional* out, const regexes& warn_detect, + string& log, optional& out, const regexes& warn_detect, const optional& bkp_step, const optional& bkp_status, string& last_cmd, @@ -568,7 +616,7 @@ run_bpkg (step_id step, { return run_cmd (step, t, - log, out, warn_detect, + log, &out, path () /* out_file */, warn_detect, "bpkg " + cmd, bkp_step, bkp_status, last_cmd, process_env ("bpkg", envvars), @@ -587,19 +635,62 @@ run_bpkg (step_id step, const char* verbosity, const string& cmd, A&&... a) { + return run_cmd (step, + t, + log, nullptr /* out */, path () /* out_file */, warn_detect, + "bpkg " + cmd, + bkp_step, bkp_status, last_cmd, + process_env ("bpkg", envvars), + verbosity, cmd, forward (a)...); +} + +template +static result_status +run_bpkg (step_id step, + tracer& t, + string& log, optional& out, const regexes& warn_detect, + const optional& bkp_step, + const optional& bkp_status, + string& last_cmd, + const char* verbosity, + const string& cmd, A&&... a) +{ + const char* const* envvars (nullptr); + return run_bpkg (step, envvars, t, - log, nullptr /* out */, warn_detect, + log, out, warn_detect, bkp_step, bkp_status, last_cmd, verbosity, cmd, forward (a)...); } +template +static result_status +run_bpkg (step_id step, + const V& envvars, + tracer& t, + string& log, const path& out, const regexes& warn_detect, + const optional& bkp_step, + const optional& bkp_status, + string& last_cmd, + const char* verbosity, + const string& cmd, A&&... a) +{ + return run_cmd (step, + t, + log, nullptr /* out_file */, out, warn_detect, + "bpkg " + cmd, + bkp_step, bkp_status, last_cmd, + process_env ("bpkg", envvars), + verbosity, cmd, forward (a)...); +} + template static result_status run_bpkg (step_id step, tracer& t, - string& log, optional* out, const regexes& warn_detect, + string& log, const path& out, const regexes& warn_detect, const optional& bkp_step, const optional& bkp_status, string& last_cmd, @@ -659,8 +750,9 @@ run_b (step_id step, } return run_cmd (step, - t, - log, nullptr /* out */, warn_detect, + t, log, + nullptr /* out_str */, path () /* out_file */, + warn_detect, name, bkp_step, bkp_status, last_cmd, process_env ("b", envvars), @@ -680,8 +772,9 @@ run_b (step_id step, const string& buildspec, A&&... a) { return run_cmd (step, - t, - log, nullptr /* out */, warn_detect, + t, log, + nullptr /* out_str */, path () /* out_file */, + warn_detect, "b " + buildspec, bkp_step, bkp_status, last_cmd, process_env ("b", envvars), @@ -868,11 +961,36 @@ build (size_t argc, const char* argv[]) { auto fail_operation = [&trace] (operation_result& r, const string& e, - result_status s) + result_status s, + const string& name = "", + uint64_t line = 0, + uint64_t column = 0) { - l3 ([&]{trace << e;}); + string prefix; + + if (!name.empty ()) + { + prefix += name; + prefix += ':'; + + if (line != 0) + { + prefix += to_string (line); + prefix += ':'; - r.log += "error: " + e + '\n'; + if (column != 0) + { + prefix += to_string (column); + prefix += ':'; + } + } + + prefix += ' '; + } + + l3 ([&]{trace << prefix << e;}); + + r.log += prefix + "error: " + e + '\n'; r.status = s; }; @@ -971,6 +1089,10 @@ build (size_t argc, const char* argv[]) prefix != "bpkg.test-separate.update" && prefix != "bpkg.test-separate.test" && prefix != "bpkg.install" && + prefix != "bpkg.bindist.debian" && + prefix != "bpkg.bindist.fedora" && + prefix != "bpkg.bindist.archive" && + prefix != "bbot.sys-install" && prefix != "b.test-installed.test" && prefix != "bpkg.test-separate-installed.update" && prefix != "bpkg.test-separate-installed.test") @@ -997,15 +1119,11 @@ build (size_t argc, const char* argv[]) // Return true if the step is explicitly enabled via a +:[] // environment/configuration argument. // - // @@ TMP Use it for bpkg.bindist step. - // -#if 0 auto step_enabled = [&step_statuses] (step_id step) -> bool { auto i (step_statuses.find (to_string (step))); return i != step_statuses.end () && i->second; }; -#endif // Return true if the step is explicitly disabled via a -:[] // environment/configuration argument. @@ -1016,6 +1134,23 @@ build (size_t argc, const char* argv[]) return i != step_statuses.end () && !i->second; }; + // Save a step status. + // + // Note that since the bpkg.bindist.* steps are mutually exclusive we only + // keep the latest status change (see above for details). + // + auto step_status = [&step_statuses] (const string& step, bool status) + { + if (step.compare (0, 13, "bpkg.bindist.") == 0) + { + step_statuses.erase ("bpkg.bindist.debian"); + step_statuses.erase ("bpkg.bindist.fedora"); + step_statuses.erase ("bpkg.bindist.archive"); + } + + step_statuses[step] = status; + }; + // Parse the environment, target configuration, and build package // configuration arguments. // @@ -1056,7 +1191,7 @@ build (size_t argc, const char* argv[]) fail << "invalid module prefix in '" << a << "'"; if (v->step_status) - step_statuses[v->prefix] = *v->step_status; + step_status (v->prefix, *v->step_status); if (v->value) (mod ? modules : env_args).emplace (make_pair (move (v->prefix), @@ -1091,7 +1226,7 @@ build (size_t argc, const char* argv[]) } if (v->step_status) - step_statuses[v->prefix] = *v->step_status; + step_status (v->prefix, *v->step_status); if (v->value) tgt_args.emplace (make_pair (move (v->prefix), move (*v->value))); @@ -1176,7 +1311,7 @@ build (size_t argc, const char* argv[]) '\''); if (v->step_status) - step_statuses[v->prefix] = *v->step_status; + step_status (v->prefix, *v->step_status); if (v->value) pkg_args.emplace (make_pair (move (v->prefix), @@ -1525,8 +1660,9 @@ build (size_t argc, const char* argv[]) // packages because the assumption is that they have been built/run (and // with buildtab settings such as warnings, etc) when testing the // self-hosted configuration this non-self-hosted one is based on. Also, - // by the same reason, we don't install tools or modules for - // non-self-hosted configurations. + // by the same reason, we don't install tools or modules nor generate the + // binary distribution packages for them for non-self-hosted + // configurations. // // Actually, it could make sense to build and install tools and module // from a target configuration in this case. But that means for a @@ -1569,10 +1705,28 @@ build (size_t argc, const char* argv[]) bool host_pkg (!module_pkg && requirement ("host")); bool target_pkg (!module_pkg && !host_pkg); - // Search for config.install.root variable. If it is present and has a - // non-empty value, then test the package installation and uninstall. Note - // that passing [null] value would be meaningless, so we don't recognize - // it as a special one. + // Don't generate binary packages for tools or modules for non-self-hosted + // configurations (see above for details). + // + optional bindist; + + if (target_pkg || selfhost) + { + if (step_enabled (step_id::bpkg_bindist_debian)) + bindist = step_id::bpkg_bindist_debian; + else if (step_enabled (step_id::bpkg_bindist_fedora)) + bindist = step_id::bpkg_bindist_fedora; + else if (step_enabled (step_id::bpkg_bindist_archive)) + bindist = step_id::bpkg_bindist_archive; + } + + bool sys_install (bindist && !step_disabled (step_id::bbot_sys_install)); + + // Unless a bpkg.bindist.* step is enabled or bpkg.install step is + // disabled, search for config.install.root variable. If it is present and + // has a non-empty value, then test the package installation and + // uninstall. Note that passing [null] value would be meaningless, so we + // don't recognize it as a special one. // // Note that the host package can only be installed for a self-hosted // configuration, using bpkg configuration of the target type. @@ -1586,7 +1740,9 @@ build (size_t argc, const char* argv[]) // optional install_root; - if (target_pkg || selfhost) + if ((target_pkg || selfhost) && + !bindist && + !step_disabled (step_id::bpkg_install)) { if (!module_pkg) { @@ -1704,10 +1860,11 @@ build (size_t argc, const char* argv[]) // bool create_module (module_pkg || (host_pkg && has_buildtime_tests)); - // Create the configuration for installing the main package of the host or + // Create the configuration for installing the main package (potentially + // as a part of generating binary distribution package) of the host or // module type, unless it's not supposed to be installed. // - bool create_install (!target_pkg && install_root); + bool create_install (!target_pkg && (install_root || bindist)); // Root configuration through which we will be configuring the cluster // (note: does not necessarily match the main package type). @@ -2720,7 +2877,7 @@ build (size_t argc, const char* argv[]) r.status |= run_bpkg ( b, - trace, r.log, &dependency_checksum, wre, + trace, r.log, dependency_checksum, wre, bkp_step, bkp_status, last_cmd, "-v", "build", @@ -3166,67 +3323,337 @@ build (size_t argc, const char* argv[]) break; } - // Install the package, optionally test the installation and uninstall - // afterwards. + // Install from source. // - // 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) + { + install_conf = rwd / (create_install ? install_conf : main_pkg_conf); + + operation_result& r (add_result ("install")); + + 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. + // + if (!target_pkg && create_target) + rm_r (trace, &r.log, rwd / target_conf); + + // bpkg install + // + // + 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", + "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; + } + // + // Fail if the breakpoint refers to the bpkg.install step but the + // package is not supposed to be installed from source. + // + else if (bkp_step && *bkp_step == step_id::bpkg_install) + { + fail_unreached_breakpoint (add_result ("install")); + break; + } + + // Generate the binary distribution package. + // + // The following bindist_* structures contain a subset of members of the + // corresponding structures described in the STRUCTURED RESULT section + // of the bpkg-pkg-bindist(1) man page. Note: needed later for + // uninstall, upload. // - if (install_root && !step_disabled (step_id::bpkg_install)) + struct bindist_file { - // Now the overall plan is as follows: + bbot::path path; // Absolute and normalized. + string system_name; + }; + + struct bindist_package + { + vector files; + }; + + struct bindist_result_type + { + bindist_package package; + vector dependencies; + }; + + bindist_result_type bindist_result; + + if (bindist) + { + operation_result& r (add_result ("bindist")); + + // Fail if the breakpoint refers to a bpkg.bindist.* step but this + // step differs from the enabled one. // - // 1. Install the package. + if (bkp_step && + (*bkp_step == step_id::bpkg_bindist_debian || + *bkp_step == step_id::bpkg_bindist_fedora || + *bkp_step == step_id::bpkg_bindist_archive) && + *bkp_step != *bindist) + { + fail_unreached_breakpoint (r); + break; + } + + change_wd (trace, &r.log, rwd); + + const dir_path& bindist_conf ( + create_install ? install_conf : main_pkg_conf); + + // Make it absolute for the sake of diagnostics. // - // 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. + path out_file (rwd / bindist_conf / "bindist-result.json"); + + string distribution; + dir_path output_root; + + switch (*bindist) + { + case step_id::bpkg_bindist_debian: + { + distribution = "debian"; + output_root = dir_path ("bindist"); + break; + } + case step_id::bpkg_bindist_fedora: + { + distribution = "fedora"; + break; + } + case step_id::bpkg_bindist_archive: + { + distribution = "archive"; + output_root = dir_path ("bindist"); + break; + } + default: assert (false); + } + + // bpkg bindist --distribution + // + // // - // 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. + // Note that if we are installing the result, we need to generate + // packages for all the dependencies unless they are included in the + // package (with --recursive). The way we are going to arrange for + // this is by specifying --recursive=separate first and letting any + // user --recursive option override that. // - // 4. Uninstall the package. + step_id b (*bindist); + step_id s (*bindist); - install_conf = rwd / (create_install ? install_conf : main_pkg_conf); + r.status |= run_bpkg ( + b, + trace, r.log, out_file, wre, + bkp_step, bkp_status, last_cmd, + "-v", + "bindist", + "--distribution", distribution, + sys_install ? cstrings ({"--recursive", "separate"}) : cstrings (), + "--structured-result", "json", + (!output_root.empty () + ? cstrings ({"--output-root", output_root.string ().c_str ()}) + : cstrings ()), + "-d", bindist_conf, + step_args (env_args, s), + step_args (tgt_args, s), + step_args (pkg_args, s), + pkg); - // Install. + if (!r.status) + break; + + // Parse the structured result JSON. // + try { - operation_result& r (add_result ("install")); + ifdstream is (out_file); + json::parser p (is, out_file.string ()); + + using event = json::event; - change_wd (trace, &r.log, install_conf); + auto bad_json = [&p] (string d) + { + throw json::invalid_json_input (p.input_name, + p.line (), + p.column (), + p.position (), + move (d)); + }; - // 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. + // Parse a bindist_file object. // - if (!target_pkg && create_target) - rm_r (trace, &r.log, rwd / target_conf); + auto parse_file = [&p, &bad_json] () + { + // enter: after begin_object + // leave: after end_object + + bindist_file r; + + // Skip unknown/uninteresting members. + // + while (p.next_expect (event::name, event::end_object)) + { + const string& n (p.name ()); - // bpkg install - // + if (n == "path") + { + try + { + r.path = + path (p.next_expect_string ()).complete ().normalize (); + } + catch (const invalid_path& e) + { + bad_json ("invalid package file path '" + e.path + "'"); + } + } + else if (n == "system_name") + { + r.system_name = p.next_expect_string (); + } + else + p.next_expect_value_skip (); + } + + return r; + }; + + // Parse a bindist_package object. // - step_id b (step_id::bpkg_install); - step_id s (step_id::bpkg_install); + auto parse_package = [&p, &parse_file] () + { + // enter: after begin_object + // leave: after end_object - 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), - step_args (pkg_args, s), - pkg); + bindist_package r; - if (!r.status) - break; + // Skip unknown/uninteresting members. + // + while (p.next_expect (event::name, event::end_object)) + { + const string& n (p.name ()); - rm.status |= r.status; + if (n == "files") + { + p.next_expect (event::begin_array); + + while (p.next_expect (event::begin_object, event::end_array)) + r.files.push_back (parse_file ()); + } + else + p.next_expect_value_skip (); + } + + return r; + }; + + // Parse the bindist_result. + // + p.next_expect (event::begin_object); + + while (p.next_expect (event::name, event::end_object)) + { + const string& n (p.name ()); + + if (n == "distribution") + { + string dist (p.next_expect_string ()); + + if (dist != distribution) + bad_json ("expected distribution '" + distribution + + "' instead of '" + dist + "'"); + } + else if (n == "package") + { + p.next_expect (event::begin_object); + bindist_result.package = parse_package (); + } + else if (n == "dependencies") + { + p.next_expect (event::begin_array); + + while (p.next_expect (event::begin_object, event::end_array)) + bindist_result.dependencies.push_back (parse_package ()); + } + else + p.next_expect_value_skip (); + } + } + catch (const json::invalid_json_input& e) + { + fail_operation ( + r, + string ("invalid bpkg-pkg-bindist result json input: ") + + e.what (), + result_status::abort, + e.name, + e.line, + e.column); + + // Fall through. } + catch (const io_error& e) + { + fail << "unable to read " << out_file << ": " << e; + } + + if (!r.status) + break; + + log_line ("generated " + distribution + " package for " + pkg + '/' + + ver.string () + ':', + r.log); + + for (const bindist_file& f: bindist_result.package.files) + log_line (" " + f.path.string (), r.log); + } + // + // Fail if the breakpoint refers to a bpkg.bindist.* step but this step + // is disabled. + // + else if (bkp_step && + (*bkp_step == step_id::bpkg_bindist_debian || + *bkp_step == step_id::bpkg_bindist_fedora || + *bkp_step == step_id::bpkg_bindist_archive)) + { + fail_unreached_breakpoint (add_result ("bindist")); + break; + } + // Now, if the package is installed, either from source or from the + // binary distribution package, the overall plan is as follows: + // + // 1. 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. + // + // 2. If any of the test packages are specified, then configure, build, + // and test them in a separate bpkg configuration(s) against the + // installed package. + // + if (install_root /*|| sys_install */) // @@ TMP + { // Run the internal tests if the project contains "testable" // subprojects, but not for a module. // @@ -3268,7 +3695,7 @@ build (size_t argc, const char* argv[]) // small_vector envvars; - if (!module_pkg) + if (!module_pkg && install_root) { // Note that we add the $config.install.root/bin directory at the // beginning of the PATH environment variable value, so the @@ -3957,48 +4384,57 @@ build (size_t argc, const char* argv[]) fail_unreached_breakpoint (add_result ("test-installed")); break; } + } + // + // Fail if the breakpoint refers to some of the test installed steps but + // the package is not supposed to be installed neither from source nor + // from the binary distribution package. + // + else if (bkp_step && + *bkp_step >= step_id::b_test_installed_create && + *bkp_step <= step_id::bpkg_test_separate_installed_test) + { + fail_unreached_breakpoint (add_result ("install")); + break; + } - // Uninstall. - // - { - operation_result& r (add_result ("uninstall")); + // Uninstall, if installed from source. + // + if (install_root) + { + operation_result& r (add_result ("uninstall")); - change_wd (trace, &r.log, install_conf); + change_wd (trace, &r.log, install_conf); - // bpkg uninstall - // - // - step_id b (step_id::bpkg_uninstall); - step_id s (step_id::bpkg_uninstall); + // bpkg uninstall + // + // + 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), + step_args (pkg_args, s), + pkg); - if (!r.status) - break; + if (!r.status) + break; - rm.status |= 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. + // Fail if the breakpoint refers to the bpkg.uninstall step but the + // package was not installed from source. // - else if (bkp_step && - *bkp_step >= step_id::bpkg_install && - *bkp_step <= step_id::bpkg_uninstall) + else if (bkp_step && *bkp_step == step_id::bpkg_uninstall) { - fail_unreached_breakpoint (add_result ("install")); + fail_unreached_breakpoint (add_result ("uninstall")); break; } } @@ -4032,7 +4468,9 @@ build (size_t argc, const char* argv[]) if (!error) { r.status |= run_cmd (step_id::end, - trace, r.log, nullptr /* out */, regexes (), + trace, r.log, + nullptr /* out_str */, path () /* out_file */, + regexes (), "" /* name */, bkp_step, bkp_status, last_cmd, process_env ()); -- cgit v1.1