aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-04-04 01:10:44 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-04-06 16:19:54 +0300
commitb92c1c200deef9a4e2bbd686080ecc4c64c22d10 (patch)
treeba84aa43cdacd678bf8fe082f16b2735731531df
parentab8a3a6c226e047afeda08423ff5d8873631314d (diff)
Add support for bpkg.bindist.* step ids
-rw-r--r--bbot/worker/worker.cxx716
-rw-r--r--doc/manual.cli91
-rw-r--r--tests/integration/testscript51
3 files changed, 706 insertions, 152 deletions
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 <libbutl/filesystem.hxx>
#include <libbutl/string-parser.hxx>
+#include <libbutl/json/parser.hxx>
+
#include <libbbot/manifest.hxx>
#include <bbot/types.hxx>
@@ -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<regex>;
+// 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 <typename... A>
static result_status
run_cmd (step_id step,
tracer& t,
- string& log, optional<string>* out, const regexes& warn_detect,
+ string& log,
+ optional<string>* out_str, const path& out_file,
+ const regexes& warn_detect,
const string& name,
const optional<step_id>& bkp_step,
const optional<result_status>& 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> (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> (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<string>* out, const regexes& warn_detect,
+ string& log, optional<string>& out, const regexes& warn_detect,
const optional<step_id>& bkp_step,
const optional<result_status>& 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> (a)...);
+}
+
+template <typename... A>
+static result_status
+run_bpkg (step_id step,
+ tracer& t,
+ string& log, optional<string>& out, const regexes& warn_detect,
+ const optional<step_id>& bkp_step,
+ const optional<result_status>& 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> (a)...);
}
+template <typename V, typename... A>
+static result_status
+run_bpkg (step_id step,
+ const V& envvars,
+ tracer& t,
+ string& log, const path& out, const regexes& warn_detect,
+ const optional<step_id>& bkp_step,
+ const optional<result_status>& 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> (a)...);
+}
+
template <typename... A>
static result_status
run_bpkg (step_id step,
tracer& t,
- string& log, optional<string>* out, const regexes& warn_detect,
+ string& log, const path& out, const regexes& warn_detect,
const optional<step_id>& bkp_step,
const optional<result_status>& 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 +<prefix>:[<value>]
// 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 -<prefix>:[<value>]
// 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<step_id> 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<dir_path> 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 <env-config-args> <tgt-config-args> <pkg-config-args>
+ // <package-name>
+ //
+ 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<bindist_file> files;
+ };
+
+ struct bindist_result_type
+ {
+ bindist_package package;
+ vector<bindist_package> 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 <distribution>
+ // <env-config-args> <tgt-config-args> <pkg-config-args>
+ // <package-name>
//
- // 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 <env-config-args> <tgt-config-args> <pkg-config-args>
- // <package-name>
+ 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<string, 1> 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 <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> <pkg-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),
+ 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 ());
diff --git a/doc/manual.cli b/doc/manual.cli
index 8b64687..5c76c32 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -1060,13 +1060,29 @@ bpkg -v update <package-name>
bpkg -v test <package-name>
}
-# If config.install.root is specified:
+# If config.install.root is specified and no
+# bpkg.bindist.{debian,fedora,archive} step is enabled:
#
{
# bpkg.install
#
bpkg -v install <package-name>
+}
+
+# If bpkg.bindist.{debian,fedora,archive} step is enabled:
+#
+{
+ # bpkg.bindist.{debian,fedora,archive}
+ #
+ bpkg -v bindist --distribution <distribution> \\
+ <env-config-args> <tgt-config-args> <pkg-config-args> \\
+ <package-name>
+}
+# If the main package is installed either from source or from the
+# binary distribution package:
+#
+{
# If the package contains subprojects that support the test
# operation:
#
@@ -1090,7 +1106,7 @@ bpkg -v update <package-name>
}
# If task manifest refers to any (runtime) tests, examples, or
- # benchmarks packages.
+ # benchmarks packages:
#
{
# bpkg.test-separate-installed.create (
@@ -1134,7 +1150,11 @@ bpkg -v update <package-name>
bpkg -v test <package-name>
}
}
+}
+# If the main package is installed from source:
+#
+{
# bpkg.uninstall
#
bpkg -v uninstall <package-name>
@@ -1315,13 +1335,30 @@ bpkg -v update -d <host-conf> <package-name>
bpkg -v test -d <target-conf> <package-name>
}
-# If configuration is self-hosted and config.install.root is specified:
+# If configuration is self-hosted, config.install.root is specified,
+# and no bpkg.bindist.{debian,fedora,archive} step is enabled:
#
{
# bpkg.install
#
bpkg -v install -d <install-conf> <package-name>
+}
+# If configuration is self-hosted and
+# bpkg.bindist.{debian,fedora,archive} step is enabled:
+#
+{
+ # bpkg.bindist.{debian,fedora,archive}
+ #
+ bpkg -v bindist --distribution <distribution> \\
+ <env-config-args> <tgt-config-args> <pkg-config-args> \\
+ <package-name>
+}
+
+# If the main package is installed either from source or from the
+# binary distribution package:
+#
+{
# If the package contains subprojects that support the test
# operation:
#
@@ -1437,7 +1474,11 @@ bpkg -v update -d <host-conf> <package-name>
bpkg -v test <package-name>
}
}
+}
+# If the main package is installed from source:
+#
+{
# bpkg.uninstall
#
bpkg -v uninstall -d <install-conf> <package-name>
@@ -1591,13 +1632,30 @@ bpkg -v update -d <module-conf> <package-name>
bpkg -v test -d <target-conf> <package-name>
}
-# If configuration is self-hosted and config.install.root is specified:
+# If configuration is self-hosted, config.install.root is specified,
+# and no bpkg.bindist.{debian,fedora,archive} step is enabled:
#
{
# bpkg.install
#
bpkg -v install -d <install-conf> <package-name>
+}
+# If configuration is self-hosted and
+# bpkg.bindist.{debian,fedora,archive} step is enabled:
+#
+{
+ # bpkg.bindist.{debian,fedora,archive}
+ #
+ bpkg -v bindist --distribution <distribution> \\
+ <env-config-args> <tgt-config-args> <pkg-config-args> \\
+ <package-name>
+}
+
+# If the main package is installed either from source or from the
+# binary distribution package:
+#
+{
# If task manifest refers to any (build-time) tests, examples, or
# benchmarks packages:
#
@@ -1665,7 +1723,11 @@ bpkg -v update -d <module-conf> <package-name>
bpkg -v test -d <target-conf> <package-name>
}
}
+}
+# If the main package is installed from source:
+#
+{
# bpkg.uninstall
#
bpkg -v uninstall -d <install-conf> <package-name>
@@ -1781,14 +1843,27 @@ bpkg.update
bpkg.test
bpkg.test-separate.update
bpkg.test-separate.test
-bpkg.bindist # Disabled by default.
-bpkg.install # Disabled if bpkg.bindist is enabled.
-bbot.sys-install # Disabled if bpkg.bindist is disabled.
+
+# Disabled if bpkg.bindist.* is enabled.
+#
+bpkg.install
+
+# Disabled by default.
+#
+bpkg.bindist.{debian,fedora,archive}
+
+# Disabled if bpkg.bindist.* is disabled.
+#
+bbot.sys-install
+
b.test-installed.test
bpkg.test-separate-installed.update
bpkg.test-separate-installed.test
\
+Note that the \c{bpkg.bindist.*} steps are mutually exclusive and only the
+last step status change via the \c{(+|-)bpkg.bindist.*} prefix is considered.
+
Unprefixed values only apply to the \c{*.create[_for_*]} steps. Note that
options with values can only be specified using the single argument
notation. For example:
@@ -1796,7 +1871,7 @@ notation. For example:
\
bpkg:--fetch-timeout=600 \\
bpkg.configure.fetch:--fetch-timeout=60 \\
-+bpkg.bindist: \\
++bpkg.bindist.debian: \\
b:-j1
\
diff --git a/tests/integration/testscript b/tests/integration/testscript
index 5c14d73..4da46c1 100644
--- a/tests/integration/testscript
+++ b/tests/integration/testscript
@@ -60,6 +60,19 @@ rep_type = pkg
rfp = yes
#host='host: true'
#dependency_checksum = 'dependency-checksum: e6f10587696020674c260669f4e7000a0139df72467bff9770aea2f2b8b57ba0'
+
+#package_config = 'package-config: -bpkg.install:'
+
+#\
+package_config = 'package-config:
+\
++bpkg.bindist.fedora:
+bpkg.create:config.bin.rpath=[null]
+\'
+#\
+
+#+bpkg.bindist.archive:--archive-lang=cc=gcc11
+
#\
package_config = 'package-config:\
bpkg.configure.fetch:--fetch-timeout=120 -bpkg.install:
@@ -90,7 +103,7 @@ rfp = yes
#
#\
pkg = libbuild2-hello
-ver = 0.1.0-a.0.20220111112708.b719144c077f
+ver = 0.1.0
rep_url = "https://github.com/build2/libbuild2-hello.git#master"
rep_type = git
#rep_url = https://stage.build2.org/1
@@ -99,6 +112,14 @@ rfp = yes
tests="tests: * libbuild2-hello-tests == $ver"
host='host: true'
#\
+#package_config = 'package-config: -bpkg.install:'
+#\
+package_config = 'package-config:
+\
++bpkg.bindist.fedora:
+bpkg.module.create:config.bin.rpath=[null]
+\'
+#\
# Use the build2 driver installed into ~/install/bin (see above).
#
@@ -115,9 +136,17 @@ requires = 'requires: bootstrap'
tests = "tests: * libbuild2-kconfig-tests == $ver
examples: * kconfig-hello == $ver"
host = 'host: true'
-package_config = 'package-config: config.libbuild2-kconfig.develop=true'
-#dependency_checksum = 'dependency-checksum: 72ae02bed9a05aaf022147297a99b84d63b712e15d05cc073551da39003e87e8'
#\
+#package_config = 'package-config: config.libbuild2-kconfig.develop=true'
+#package_config = 'package-config: -bpkg.install:'
+#\
+package_config = 'package-config:
+\
++bpkg.bindist.fedora:
+bpkg.module.create:config.bin.rpath=[null]
+\'
+#\
+#dependency_checksum = 'dependency-checksum: 72ae02bed9a05aaf022147297a99b84d63b712e15d05cc073551da39003e87e8'
# Use the build2 driver installed into ~/install/bin (see above).
#
@@ -166,8 +195,16 @@ requires='requires: host'
tests="tests: * cli-tests == $ver
examples: * cli-examples == $ver"
host='host: true'
-#package_config = 'package-config: ?libcutl +{ config.libcutl.develop=true }'
#\
+#package_config = 'package-config: -bpkg.install:'
+#\
+package_config = 'package-config:
+\
++bpkg.bindist.fedora:
+bpkg.create:config.bin.rpath=[null]
+\'
+#\
+#package_config = 'package-config: ?libcutl +{ config.libcutl.develop=true }'
#\
pkg = libxsd
@@ -192,9 +229,13 @@ requires='requires: host'
tests="tests: * xsd-tests == $ver
examples: * xsd-examples == $ver"
host='host: true'
+#\
+#\
package_config = 'package-config:
\
?sys:libxerces-c --sys-install --sys-yes
++bpkg.bindist.fedora:
+bpkg.create:config.bin.rpath=[null]
\'
#\
#package_config = 'package-config:
@@ -334,7 +375,7 @@ a = $0
&?build/*** &?build-host/*** &?build-module/*** &?build-install/*** \
&?build-installed/*** &?build-installed-bpkg/*** \
&?build-installed-bpkg-module/*** &?build-installed-bpkg-host/*** \
- &?dist/*** &?redist/*** \
+ &?dist/*** &?redist/*** &?bindist/*** \
&?dist-install/*** &?redist-install/*** \
&?dist-installed/*** &?redist-installed/*** \
&task.manifest <| 2>|