aboutsummaryrefslogtreecommitdiff
path: root/libbbot
diff options
context:
space:
mode:
Diffstat (limited to 'libbbot')
-rw-r--r--libbbot/build-target-config.cxx (renamed from libbbot/build-config.cxx)60
-rw-r--r--libbbot/build-target-config.hxx (renamed from libbbot/build-config.hxx)29
-rw-r--r--libbbot/manifest.cxx499
-rw-r--r--libbbot/manifest.hxx241
4 files changed, 702 insertions, 127 deletions
diff --git a/libbbot/build-config.cxx b/libbbot/build-target-config.cxx
index ff365e3..9e04a17 100644
--- a/libbbot/build-config.cxx
+++ b/libbbot/build-target-config.cxx
@@ -1,7 +1,7 @@
-// file : libbbot/build-config.cxx -*- C++ -*-
+// file : libbbot/build-target-config.cxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#include <libbbot/build-config.hxx>
+#include <libbbot/build-target-config.hxx>
#include <map>
#include <string>
@@ -9,10 +9,10 @@
#include <utility> // move(), make_pair()
#include <stdexcept> // invalid_argument
-#include <libbutl/path.mxx>
-#include <libbutl/fdstream.mxx>
-#include <libbutl/tab-parser.mxx>
-#include <libbutl/string-parser.mxx>
+#include <libbutl/path.hxx>
+#include <libbutl/fdstream.hxx>
+#include <libbutl/tab-parser.hxx>
+#include <libbutl/string-parser.hxx>
#include <libbpkg/manifest.hxx> // build_class_term::validate_name()
@@ -23,10 +23,10 @@ using namespace butl;
namespace bbot
{
- LIBBBOT_EXPORT build_configs
+ LIBBBOT_EXPORT build_target_configs
parse_buildtab (istream& is, const string& name)
{
- build_configs r;
+ build_target_configs r;
tab_parser parser (is, name);
r.classes.push_back ("all");
@@ -56,33 +56,26 @@ namespace bbot
d);
};
- build_config config;
+ build_target_config config;
config.machine_pattern = move (tl[i++].value);
// If the machine pattern is a single dash character, then this is a
// placeholder entry. The only thing we are interested about it is the
// class inheritance information. Note that while all other information
- // is discarded, the configuration name and target must be present (can
- // also be dashes), so the classes field can be determined and parsed.
+ // is discarded, the target configuration name and target must be
+ // present (can also be dashes), so the classes field can be determined
+ // and parsed.
//
bool placeholder (config.machine_pattern == "-");
- // Configuration name, target[/environment] and classes fields are the
- // required ones.
+ // Target configuration name, target[/environment] and classes fields
+ // are the required ones.
//
if (i == n)
- bad_line ("no configuration name found");
+ bad_line ("no target configuration name found");
config.name = move (tl[i].value);
- // Make sure the name is unique.
- //
- for (const auto& c: r)
- {
- if (c.name == config.name)
- bad_line ("duplicate configuration name");
- }
-
if (++i == n)
bad_line ("no target found");
@@ -116,6 +109,14 @@ namespace bbot
bad_line (e.what ());
}
+ // Make sure the name/target combination is unique.
+ //
+ for (const auto& c: r)
+ {
+ if (c.name == config.name && c.target == config.target)
+ bad_line ("duplicate target configuration name/target");
+ }
+
if (++i == n)
bad_line ("no classes found");
@@ -189,9 +190,9 @@ namespace bbot
r.classes.push_back (c);
}
else if (j->second != base && !base.empty ())
- throw invalid_argument ("'" + c + "' new base '" + base +
+ throw invalid_argument ('\'' + c + "' new base '" + base +
"' does not match existing '" +
- j->second + "'");
+ j->second + '\'');
if (!placeholder)
config.classes.emplace_back (move (c));
@@ -221,7 +222,7 @@ namespace bbot
task_manifest::validate_regex (re);
config.warning_regexes.emplace_back (move (re));
}
- else // Configuration option or variable.
+ else // Target configuration option or variable.
config.args.emplace_back (move (v));
}
}
@@ -230,13 +231,14 @@ namespace bbot
bad_line (e.what ());
}
- // Save the configuration.
+ // Save the target configuration.
//
r.emplace_back (move (config));
}
// Erase entries for baseless classes (we were collecting them to make
- // sure that the class inheritance is consistent across configurations).
+ // sure that the class inheritance is consistent across target
+ // configurations).
//
for (auto i (r.class_inheritance_map.begin ());
i != r.class_inheritance_map.end (); )
@@ -250,11 +252,11 @@ namespace bbot
return r;
}
- build_configs
+ build_target_configs
parse_buildtab (const path& p)
{
ifdstream ifs (p);
- build_configs r (parse_buildtab (ifs, p.string ()));
+ build_target_configs r (parse_buildtab (ifs, p.string ()));
ifs.close (); // Throws on failure.
return r;
diff --git a/libbbot/build-config.hxx b/libbbot/build-target-config.hxx
index 3541388..27b555c 100644
--- a/libbbot/build-config.hxx
+++ b/libbbot/build-target-config.hxx
@@ -1,4 +1,4 @@
-// file : libbbot/build-config.hxx -*- C++ -*-
+// file : libbbot/build-target-config.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
#ifndef LIBBBOT_BUILD_CONFIG_HXX
@@ -9,20 +9,20 @@
#include <vector>
#include <iosfwd>
-#include <libbutl/path.mxx>
-#include <libbutl/optional.mxx>
-#include <libbutl/tab-parser.mxx>
-#include <libbutl/target-triplet.mxx>
+#include <libbutl/path.hxx>
+#include <libbutl/optional.hxx>
+#include <libbutl/tab-parser.hxx>
+#include <libbutl/target-triplet.hxx>
#include <libbbot/export.hxx>
#include <libbbot/version.hxx>
namespace bbot
{
- // Build configuration matching specific machine names. Used by bbot
+ // Build target configuration matching specific machine names. Used by bbot
// controllers.
//
- struct build_config
+ struct build_target_config
{
std::string machine_pattern; // Machine name pattern.
std::string name; // Configuration name.
@@ -36,10 +36,10 @@ namespace bbot
std::vector<std::string> warning_regexes;
};
- struct build_configs: std::vector<build_config>
+ struct build_target_configs: std::vector<build_target_config>
{
- // List of all configuration class names. Starts with the all and default
- // classes. The rest follows in the same order as in the buildtab.
+ // List of all target configuration class names. Starts with the all and
+ // default classes. The rest follows in the same order as in the buildtab.
//
std::vector<std::string> classes;
@@ -53,14 +53,17 @@ namespace bbot
//
// buildtab consists of lines in the following format:
//
- // <machine-pattern> <config> <target>[/<environment>] <classes> [<config-arg>]* [<warning-regex>]*
+ // <machine-pattern> <target-config> <target>[/<environment>] <classes> [<tgt-config-arg>]* [<warning-regex>]*
+ //
+ // Note that each <target-config>/<target> pair is expected to be unique in
+ // the buildtab.
//
using butl::tab_parsing;
- LIBBBOT_EXPORT build_configs
+ LIBBBOT_EXPORT build_target_configs
parse_buildtab (std::istream&, const std::string& name);
- LIBBBOT_EXPORT build_configs
+ LIBBBOT_EXPORT build_target_configs
parse_buildtab (const butl::path&);
}
diff --git a/libbbot/manifest.cxx b/libbbot/manifest.cxx
index c01a84b..45a4668 100644
--- a/libbbot/manifest.cxx
+++ b/libbbot/manifest.cxx
@@ -7,20 +7,23 @@
#include <vector>
#include <string>
#include <cctype> // isxdigit()
+#include <limits> // numeric_limits
#include <cassert>
#include <sstream>
#include <cstddef> // size_t
#include <utility> // move()
#include <cstdint> // uint64_t
+#include <cstdlib> // strtoull()
+#include <algorithm> // find_if()
#include <stdexcept> // invalid_argument
-#include <libbutl/regex.mxx>
-#include <libbutl/base64.mxx>
-#include <libbutl/utility.mxx> // digit()
-#include <libbutl/tab-parser.mxx>
-#include <libbutl/string-parser.mxx>
-#include <libbutl/manifest-parser.mxx>
-#include <libbutl/manifest-serializer.mxx>
+#include <libbutl/regex.hxx>
+#include <libbutl/base64.hxx>
+#include <libbutl/utility.hxx> // digit()
+#include <libbutl/tab-parser.hxx>
+#include <libbutl/string-parser.hxx>
+#include <libbutl/manifest-parser.hxx>
+#include <libbutl/manifest-serializer.hxx>
using namespace std;
using namespace butl;
@@ -36,6 +39,29 @@ namespace bbot
using butl::optional;
+ // machine_role
+ //
+ string
+ to_string (machine_role r)
+ {
+ switch (r)
+ {
+ case machine_role::build: return "build";
+ case machine_role::auxiliary: return "auxiliary";
+ }
+
+ assert (false);
+ return string ();
+ }
+
+ machine_role
+ to_machine_role (const string& s)
+ {
+ if (s == "build") return machine_role::build;
+ else if (s == "auxiliary") return machine_role::auxiliary;
+ else throw invalid_argument ("invalid machine role '" + s + '\'');
+ }
+
// result_status
//
string
@@ -43,11 +69,13 @@ namespace bbot
{
switch (s)
{
- case result_status::success: return "success";
- case result_status::warning: return "warning";
- case result_status::error: return "error";
- case result_status::abort: return "abort";
- case result_status::abnormal: return "abnormal";
+ case result_status::skip: return "skip";
+ case result_status::success: return "success";
+ case result_status::warning: return "warning";
+ case result_status::error: return "error";
+ case result_status::abort: return "abort";
+ case result_status::abnormal: return "abnormal";
+ case result_status::interrupt: return "interrupt";
}
assert (false);
@@ -57,12 +85,39 @@ namespace bbot
result_status
to_result_status (const string& s)
{
- if (s == "success") return result_status::success;
- else if (s == "warning") return result_status::warning;
- else if (s == "error") return result_status::error;
- else if (s == "abort") return result_status::abort;
- else if (s == "abnormal") return result_status::abnormal;
- else throw invalid_argument ("invalid result status '" + s + "'");
+ if (s == "skip") return result_status::skip;
+ else if (s == "success") return result_status::success;
+ else if (s == "warning") return result_status::warning;
+ else if (s == "error") return result_status::error;
+ else if (s == "abort") return result_status::abort;
+ else if (s == "abnormal") return result_status::abnormal;
+ else if (s == "interrupt") return result_status::interrupt;
+ else throw invalid_argument ("invalid result status '" + s + '\'');
+ }
+
+ // interactive_mode
+ //
+ string
+ to_string (interactive_mode m)
+ {
+ switch (m)
+ {
+ case interactive_mode::false_: return "false";
+ case interactive_mode::true_: return "true";
+ case interactive_mode::both: return "both";
+ }
+
+ assert (false);
+ return string ();
+ }
+
+ interactive_mode
+ to_interactive_mode (const string& s)
+ {
+ if (s == "false") return interactive_mode::false_;
+ else if (s == "true") return interactive_mode::true_;
+ else if (s == "both") return interactive_mode::both;
+ else throw invalid_argument ("invalid interactive mode '" + s + '\'');
}
// Utility functions
@@ -104,6 +159,29 @@ namespace bbot
return true;
}
+ // Return nullopt if the string is not a valid 64-bit unsigned integer.
+ //
+ static optional<uint64_t>
+ parse_uint64 (const string& s)
+ {
+ if (!s.empty () && s[0] != '-' && s[0] != '+') // strtoull() allows these.
+ {
+ const char* b (s.c_str ());
+ char* e (nullptr);
+ errno = 0; // We must clear it according to POSIX.
+ uint64_t v (strtoull (b, &e, 10)); // Can't throw.
+
+ if (errno != ERANGE &&
+ e == b + s.size () &&
+ v <= numeric_limits<uint64_t>::max ())
+ {
+ return static_cast<uint64_t> (v);
+ }
+ }
+
+ return nullopt;
+ }
+
// machine_header_manifest
//
machine_header_manifest::
@@ -177,6 +255,42 @@ namespace bbot
summary = move (v);
}
+ else if (n == "role")
+ {
+ if (role)
+ bad_name ("machine role redefinition");
+
+ try
+ {
+ role = to_machine_role (v);
+ }
+ catch (const invalid_argument& e)
+ {
+ bad_value (e.what ());
+ }
+ }
+ else if (n == "ram-minimum")
+ {
+ if (ram_minimum)
+ bad_name ("machine minimum RAM redefinition");
+
+ ram_minimum = parse_uint64 (v);
+
+ if (!ram_minimum || *ram_minimum == 0)
+ bad_value (
+ "machine minimum RAM should be non-zero 64-bit unsigned integer");
+ }
+ else if (n == "ram-maximum")
+ {
+ if (ram_maximum)
+ bad_name ("machine maximum RAM redefinition");
+
+ ram_maximum = parse_uint64 (v);
+
+ if (!ram_maximum || *ram_maximum == 0)
+ bad_value (
+ "machine maximum RAM should be non-zero 64-bit unsigned integer");
+ }
else
{
switch (m)
@@ -217,6 +331,21 @@ namespace bbot
s.next ("name", name);
s.next ("summary", summary);
+ if (role)
+ s.next ("role", to_string (*role));
+
+ if (ram_minimum)
+ {
+ assert (*ram_minimum != 0);
+ s.next ("ram-minimum", std::to_string (*ram_minimum));
+ }
+
+ if (ram_maximum)
+ {
+ assert (*ram_maximum != 0);
+ s.next ("ram-maximum", std::to_string (*ram_maximum));
+ }
+
if (end_of_manifest)
s.next ("", ""); // End of manifest.
}
@@ -246,6 +375,11 @@ namespace bbot
if (nv.value != "1")
bad_value ("unsupported format version");
+ // Cache the interactive login manifest value and validate whether it's
+ // allowed later, after the interactive mode is parsed.
+ //
+ optional<name_value> interactive_login_nv;
+
// Parse the task request manifest.
//
for (nv = p.next (); !nv.empty (); nv = p.next ())
@@ -288,6 +422,31 @@ namespace bbot
e.what ());
}
}
+ else if (n == "interactive-mode")
+ {
+ if (interactive_mode)
+ bad_name ("task request interactive mode redefinition");
+
+ try
+ {
+ interactive_mode = to_interactive_mode (v);
+ }
+ catch (const invalid_argument&)
+ {
+ bad_value (
+ string ("invalid task request interactive mode '" + v + '\''));
+ }
+ }
+ else if (n == "interactive-login")
+ {
+ if (interactive_login_nv)
+ bad_name ("task request interactive login redefinition");
+
+ if (v.empty ())
+ bad_value ("empty task request interactive login");
+
+ interactive_login_nv = move (nv);
+ }
else if (n == "fingerprint")
{
if (fingerprint)
@@ -298,6 +457,17 @@ namespace bbot
fingerprint = move (v);
}
+ else if (n == "auxiliary-ram")
+ {
+ if (auxiliary_ram)
+ bad_name ("auxiliary machines RAM limit redefinition");
+
+ auxiliary_ram = parse_uint64 (v);
+
+ if (!auxiliary_ram)
+ bad_value (
+ "auxiliary machines RAM limit should be 64-bit unsigned integer");
+ }
else if (!iu)
bad_name ("unknown name '" + n + "' in task request manifest");
}
@@ -313,6 +483,22 @@ namespace bbot
if (toolchain_version.empty ())
bad_value ("no task request toolchain version specified");
+ if (effective_interactive_mode () != interactive_mode_type::false_)
+ {
+ if (!interactive_login_nv)
+ bad_value ("no task request interactive login specified");
+
+ interactive_login = move (interactive_login_nv->value);
+ }
+ else if (interactive_login_nv)
+ {
+ // Restore as bad_name() uses its line/column.
+ //
+ nv = move (*interactive_login_nv);
+
+ bad_name ("interactive login specified for non-interactive mode");
+ }
+
// Parse machine header manifests.
//
for (nv = p.next (); !nv.empty (); nv = p.next ())
@@ -338,9 +524,18 @@ namespace bbot
s.next ("toolchain-name", toolchain_name);
s.next ("toolchain-version", toolchain_version.string ());
+ if (interactive_mode)
+ s.next ("interactive-mode", to_string (*interactive_mode));
+
+ if (interactive_login)
+ s.next ("interactive-login", *interactive_login);
+
if (fingerprint)
s.next ("fingerprint", *fingerprint);
+ if (auxiliary_ram)
+ s.next ("auxiliary-ram", std::to_string (*auxiliary_ram));
+
s.next ("", ""); // End of manifest.
for (const machine_header_manifest& m: machines)
@@ -451,6 +646,11 @@ namespace bbot
optional<name_value> repo_url;
optional<repository_type> repo_type;
+ // We will also cache the requires values to parse them later, after the
+ // package name is parsed.
+ //
+ vector<name_value> reqs;
+
for (nv = p.next (); !nv.empty (); nv = p.next ())
{
string& n (nv.name);
@@ -511,7 +711,7 @@ namespace bbot
}
catch (const invalid_argument&)
{
- bad_value ("invalid task repository type '" + v + "'");
+ bad_value ("invalid task repository type '" + v + '\'');
}
}
else if (n == "trust")
@@ -521,37 +721,31 @@ namespace bbot
trust.emplace_back (move (v));
}
- else if (n == "test-exclude")
+ else if (n == "requires")
+ {
+ reqs.push_back (move (nv));
+ }
+ else if (n == "tests" || n == "examples" || n == "benchmarks")
{
- size_t p (v.find ('/'));
- if (p == string::npos)
- bad_value ("invalid test exclusion package: '/' is expected");
-
- package_name pn;
-
try
{
- pn = package_name (string (v, 0, p));
+ tests.push_back (test_dependency (move (v),
+ to_test_dependency_type (n)));
}
catch (const invalid_argument& e)
{
- bad_value (string ("invalid test exclusion package name: ") +
- e.what ());
+ bad_value (e.what ());
}
+ }
+ else if (n == "dependency-checksum")
+ {
+ if (dependency_checksum)
+ bad_name ("task dependency checksum redefinition");
- bpkg::version pv;
-
- try
- {
- pv = bpkg::version (string (v, p + 1));
- }
- catch (const invalid_argument& e)
- {
- bad_value (string ("invalid test exclusion package version: ") +
- e.what ());
- }
+ if (v.empty ())
+ bad_value ("empty task dependency checksum");
- test_exclusions.push_back (package {move (pn), move (pv)});
+ dependency_checksum = move (v);
}
else if (n == "machine")
{
@@ -563,6 +757,26 @@ namespace bbot
machine = move (v);
}
+ else if (n == "auxiliary-machine" ||
+ (n.size () > 18 && n.compare (0, 18, "auxiliary-machine-") == 0))
+ {
+ if (v.empty ())
+ bad_value ("empty task auxiliary machine name");
+
+ auxiliary_machine m {move (v),
+ n.size () > 18 ? string (n, 18) : string ()};
+
+ if (find_if (auxiliary_machines.begin (), auxiliary_machines.end (),
+ [&m] (const auxiliary_machine& am)
+ {
+ return am.environment_name == m.environment_name;
+ }) != auxiliary_machines.end ())
+ {
+ bad_name ("task auxiliary machine environment redefinition");
+ }
+
+ auxiliary_machines.push_back (move (m));
+ }
else if (n == "target")
{
if (!target.empty ())
@@ -587,15 +801,45 @@ namespace bbot
environment = move (v);
}
- else if (n == "config")
+ else if (n == "auxiliary-environment")
{
- if (!config.empty ())
- bad_name ("task configuration redefinition");
+ if (auxiliary_environment)
+ bad_name ("task auxiliary environment redefinition");
- config = parse_tab (v, [](const string&){}, "configuration");
+ if (v.empty ())
+ bad_value ("empty task auxiliary environment");
- if (config.empty ())
- bad_value ("empty task configuration");
+ auxiliary_environment = move (v);
+ }
+ else if (n == "config" || // @@ TMP Until toolchain 0.16.0 is released.
+ n == "target-config")
+ {
+ if (!target_config.empty ())
+ bad_name ("task target configuration redefinition");
+
+ target_config = parse_tab (v, [](const string&){}, "configuration");
+
+ if (target_config.empty ())
+ bad_value ("empty task target configuration");
+ }
+ else if (n == "package-config")
+ {
+ if (!package_config.empty ())
+ bad_name ("task package configuration redefinition");
+
+ package_config = move (v);
+ }
+ else if (n == "host")
+ {
+ if (host)
+ bad_name ("task host value redefinition");
+
+ if (v == "true")
+ host = true;
+ else if (v == "false")
+ host = false;
+ else
+ bad_value ("invalid task host value '" + v + '\'');
}
else if (n == "warning-regex")
{
@@ -607,6 +851,26 @@ namespace bbot
if (warning_regex.empty ())
bad_value ("empty task warning regex");
}
+ else if (n == "interactive")
+ {
+ if (interactive)
+ bad_name ("task interactive value redefinition");
+
+ if (v.empty ())
+ bad_value ("empty task interactive value");
+
+ interactive = move (v);
+ }
+ else if (n == "worker-checksum")
+ {
+ if (worker_checksum)
+ bad_name ("task worker checksum redefinition");
+
+ if (v.empty ())
+ bad_value ("empty task worker checksum");
+
+ worker_checksum = move (v);
+ }
else if (!iu)
bad_name ("unknown name '" + n + "' in task manifest");
}
@@ -642,6 +906,18 @@ namespace bbot
nv = move (*repo_url); // Restore as bad_value() uses its line/column.
bad_value (string ("invalid task repository URL: ") + e.what ());
}
+
+ // Parse the requirements.
+ //
+ for (const name_value& r: reqs)
+ {
+ requirements.push_back (
+ requirement_alternatives (r.value,
+ name,
+ p.name (),
+ r.value_line,
+ r.value_column));
+ }
}
void task_manifest::
@@ -672,15 +948,31 @@ namespace bbot
for (const string& v: trust)
s.next ("trust", v);
- for (const package& te: test_exclusions)
- s.next ("test-exclude", te.name.string () + '/' + te.version.string ());
+ for (const requirement_alternatives& r: requirements)
+ s.next ("requires", r.string ());
+
+ for (const test_dependency& t: tests)
+ s.next (to_string (t.type), t.string ());
+
+ if (dependency_checksum)
+ s.next ("dependency-checksum", *dependency_checksum);
s.next ("machine", machine);
+
+ for (const auxiliary_machine& am: auxiliary_machines)
+ s.next ((!am.environment_name.empty ()
+ ? "auxiliary-machine-" + am.environment_name
+ : "auxiliary-machine"),
+ am.name);
+
s.next ("target", target.string ());
if (environment)
s.next ("environment", *environment);
+ if (auxiliary_environment)
+ s.next ("auxiliary-environment", *auxiliary_environment);
+
// Serialize an optional value of the strings type as a space-separated
// string list.
//
@@ -701,16 +993,35 @@ namespace bbot
}
};
- serialize_list ("config", config);
+ // @@ TMP Always use 'target-config' name and always serialize
+ // package_config after toolchain 0.16.0 is released.
+ //
+ if (!package_config.empty ())
+ {
+ serialize_list ("target-config", target_config);
+ s.next ("package-config", package_config);
+ }
+ else
+ serialize_list ("config", target_config);
+
+ if (host)
+ s.next ("host", *host ? "true" : "false");
+
serialize_list ("warning-regex", warning_regex);
+ if (interactive)
+ s.next ("interactive", *interactive);
+
+ if (worker_checksum)
+ s.next ("worker-checksum", *worker_checksum);
+
s.next ("", ""); // End of manifest.
}
strings task_manifest::
- unquoted_config () const
+ unquoted_target_config () const
{
- return string_parser::unquote (config);
+ return string_parser::unquote (target_config);
}
strings task_manifest::
@@ -798,6 +1109,33 @@ namespace bbot
result_url = move (v);
}
+ else if (n.size () > 11 &&
+ n.compare (n.size () - 11, 11, "-upload-url") == 0)
+ {
+ n.resize (n.size () - 11);
+
+ if (find_if (upload_urls.begin (), upload_urls.end (),
+ [&n] (const upload_url& u) {return u.type == n;}) !=
+ upload_urls.end ())
+ {
+ bad_name ("task response upload url redefinition");
+ }
+
+ if (v.empty ())
+ bad_value ("empty task response upload url");
+
+ upload_urls.emplace_back (move (v), move (n));
+ }
+ else if (n == "agent-checksum")
+ {
+ if (agent_checksum)
+ bad_name ("task response agent checksum redefinition");
+
+ if (v.empty ())
+ bad_value ("empty task response agent checksum");
+
+ agent_checksum = move (v);
+ }
else if (!iu)
bad_name ("unknown name '" + n + "' in task response manifest");
}
@@ -825,10 +1163,16 @@ namespace bbot
if (result_url)
bad_value ("unexpected task response result url");
+
+ if (!upload_urls.empty ())
+ bad_value ("unexpected task response upload url");
+
+ if (agent_checksum)
+ bad_value ("unexpected task response agent checksum");
}
- // If session is not empty then the task manifest must follow, otherwise it
- // shouldn't.
+ // If session is not empty then the task manifest must follow, otherwise
+ // it shouldn't.
//
nv = p.next ();
@@ -864,6 +1208,12 @@ namespace bbot
if (result_url)
s.next ("result-url", *result_url);
+ for (const upload_url& u: upload_urls)
+ s.next (u.type + "-upload-url", u.url);
+
+ if (agent_checksum)
+ s.next ("agent-checksum", *agent_checksum);
+
s.next ("", ""); // End of manifest.
if (task)
@@ -1030,6 +1380,26 @@ namespace bbot
//
results[nlog++].log = move (v);
}
+ else if (n == "worker-checksum")
+ {
+ if (worker_checksum)
+ bad_name ("result worker checksum redefinition");
+
+ if (v.empty ())
+ bad_value ("empty result worker checksum");
+
+ worker_checksum = move (v);
+ }
+ else if (n == "dependency-checksum")
+ {
+ if (dependency_checksum)
+ bad_name ("result dependency checksum redefinition");
+
+ if (v.empty ())
+ bad_value ("empty result dependency checksum");
+
+ dependency_checksum = move (v);
+ }
else if (!iu)
bad_name ("unknown name '" + n + "' in result manifest");
}
@@ -1086,6 +1456,12 @@ namespace bbot
for (const auto& r: results)
s.next (r.operation + "-log", r.log);
+ if (worker_checksum)
+ s.next ("worker-checksum", *worker_checksum);
+
+ if (dependency_checksum)
+ s.next ("dependency-checksum", *dependency_checksum);
+
s.next ("", ""); // End of manifest.
}
@@ -1148,6 +1524,16 @@ namespace bbot
bad_value ("invalid result request challenge");
}
}
+ else if (n == "agent-checksum")
+ {
+ if (!agent_checksum.empty ())
+ bad_name ("result request agent checksum redefinition");
+
+ if (v.empty ())
+ bad_value ("empty result request agent checksum");
+
+ agent_checksum = move (v);
+ }
else if (!iu)
bad_name ("unknown name '" + n + "' in result request manifest");
}
@@ -1157,6 +1543,9 @@ namespace bbot
if (session.empty ())
bad_value ("no result request session specified");
+ if (agent_checksum.empty ())
+ bad_value ("no result request agent checksum specified");
+
nv = p.next ();
if (nv.empty ())
bad_value ("result manifest expected");
@@ -1183,6 +1572,8 @@ namespace bbot
if (challenge)
s.next ("challenge", base64_encode (*challenge));
+ s.next ("agent-checksum", agent_checksum);
+
s.next ("", ""); // End of manifest.
result.serialize (s);
diff --git a/libbbot/manifest.hxx b/libbbot/manifest.hxx
index daee743..909d47d 100644
--- a/libbbot/manifest.hxx
+++ b/libbbot/manifest.hxx
@@ -6,12 +6,13 @@
#include <string>
#include <vector>
+#include <cstdint> // uint*_t
#include <ostream>
-#include <libbutl/optional.mxx>
-#include <libbutl/small-vector.mxx>
-#include <libbutl/target-triplet.mxx>
-#include <libbutl/standard-version.mxx>
+#include <libbutl/optional.hxx>
+#include <libbutl/small-vector.hxx>
+#include <libbutl/target-triplet.hxx>
+#include <libbutl/standard-version.hxx>
#include <libbutl/manifest-forward.hxx>
#include <libbpkg/manifest.hxx> // version, repository_location
@@ -24,15 +25,57 @@ namespace bbot
{
using strings = std::vector<std::string>;
+ // The machine's role.
+ //
+ enum class machine_role: std::uint8_t
+ {
+ build,
+ auxiliary
+ };
+
+ LIBBBOT_EXPORT std::string
+ to_string (machine_role);
+
+ LIBBBOT_EXPORT machine_role
+ to_machine_role (const std::string&); // May throw invalid_argument.
+
+ inline std::ostream&
+ operator<< (std::ostream& os, machine_role r)
+ {
+ return os << to_string (r);
+ }
+
class LIBBBOT_EXPORT machine_header_manifest
{
public:
std::string id;
std::string name;
std::string summary;
+ butl::optional<machine_role> role;
+ butl::optional<std::uint64_t> ram_minimum; // In KiB, non-zero.
+ butl::optional<std::uint64_t> ram_maximum; // In KiB, non-zero.
- machine_header_manifest (std::string i, std::string n, std::string s)
- : id (std::move (i)), name (std::move (n)), summary (std::move (s)) {}
+ // Return the effective machine role. If the role is not explicitly
+ // specified, then the build role is assumed.
+ //
+ machine_role
+ effective_role () const noexcept
+ {
+ return role ? *role : machine_role::build;
+ }
+
+ machine_header_manifest (std::string i,
+ std::string n,
+ std::string s,
+ butl::optional<machine_role> r,
+ butl::optional<std::uint64_t> rmn,
+ butl::optional<std::uint64_t> rmx)
+ : id (std::move (i)),
+ name (std::move (n)),
+ summary (std::move (s)),
+ role (r),
+ ram_minimum (rmn),
+ ram_maximum (rmx) {}
public:
machine_header_manifest () = default; // VC export.
@@ -62,14 +105,40 @@ namespace bbot
using machine_header_manifests = std::vector<machine_header_manifest>;
+ // Agent's capability to perform (non-)interactive builds.
+ //
+ enum class interactive_mode: std::uint8_t
+ {
+ false_,
+ true_,
+ both
+ };
+
+ LIBBBOT_EXPORT std::string
+ to_string (interactive_mode);
+
+ LIBBBOT_EXPORT interactive_mode
+ to_interactive_mode (const std::string&); // May throw invalid_argument.
+
+ inline std::ostream&
+ operator<< (std::ostream& os, interactive_mode m)
+ {
+ return os << to_string (m);
+ }
+
class LIBBBOT_EXPORT task_request_manifest
{
public:
+ using interactive_mode_type = bbot::interactive_mode;
+
std::string agent;
std::string toolchain_name;
butl::standard_version toolchain_version;
+ butl::optional<interactive_mode_type> interactive_mode;
+ butl::optional<std::string> interactive_login;
+
// Agent's public key SHA256 fingerprint.
//
// @@ How the fingerpring for openssl public key will be produced? Seems
@@ -80,18 +149,37 @@ namespace bbot
//
butl::optional<std::string> fingerprint;
+ butl::optional<std::uint64_t> auxiliary_ram; // In KiB.
+
machine_header_manifests machines;
- task_request_manifest (std::string a,
- std::string n,
- butl::standard_version v,
- butl::optional<std::string> f,
- machine_header_manifests m)
- : agent (std::move (a)),
- toolchain_name (std::move (n)),
- toolchain_version (std::move (v)),
- fingerprint (std::move (f)),
- machines (std::move (m)) {}
+ // Return the effective interactive build mode. If the mode is not
+ // explicitly specified, then false is assumed.
+ //
+ interactive_mode_type
+ effective_interactive_mode () const noexcept
+ {
+ return interactive_mode
+ ? *interactive_mode
+ : interactive_mode_type::false_;
+ }
+
+ task_request_manifest (std::string ag,
+ std::string tn,
+ butl::standard_version tv,
+ butl::optional<interactive_mode_type> im,
+ butl::optional<std::string> il,
+ butl::optional<std::string> fp,
+ butl::optional<std::uint64_t> ar,
+ machine_header_manifests ms)
+ : agent (std::move (ag)),
+ toolchain_name (std::move (tn)),
+ toolchain_version (std::move (tv)),
+ interactive_mode (std::move (im)),
+ interactive_login (std::move (il)),
+ fingerprint (std::move (fp)),
+ auxiliary_ram (ar),
+ machines (std::move (ms)) {}
public:
task_request_manifest () = default; // VC export.
@@ -108,6 +196,14 @@ namespace bbot
bpkg::version version;
};
+ // Note: corresponds to build_auxiliary in the package manifest.
+ //
+ struct auxiliary_machine
+ {
+ std::string name;
+ std::string environment_name;
+ };
+
class LIBBBOT_EXPORT task_manifest
{
public:
@@ -123,22 +219,53 @@ namespace bbot
//
strings trust;
- // Separate tests, examples, and benchmarks packages that should be
- // excluded from building together with the primary package.
+ // The subset of the build task-relevant package manifest values (see
+ // bpkg::package_manifest for their semantics).
//
- butl::small_vector<package, 1> test_exclusions;
+ std::vector<bpkg::requirement_alternatives> requirements;
+ butl::small_vector<bpkg::test_dependency, 1> tests;
+
+ butl::optional<std::string> dependency_checksum;
std::string machine; // Build machine to use for building the package.
+ // The list of build auxiliary machines.
+ //
+ // Note that all entries in this list must have distinct environment names
+ // (with empty name being one of the possibilities).
+ //
+ // Also note that some auxiliary machines can potentially be used by the
+ // main package and some by the test packages. It is such package author's
+ // responsibility to make sure that the environment names specified in
+ // multiple package manifests do not clash. It is bbot controller's
+ // responsibility to verify that there is no clash, aborting the task if
+ // there is.
+ //
+ std::vector<auxiliary_machine> auxiliary_machines;
+
butl::target_triplet target; // Build target.
butl::optional<std::string> environment; // Build environment name.
+ // The environment variables describing the auxiliary machines.
+ //
+ butl::optional<std::string> auxiliary_environment;
+
// Build system configuration variables (in addition to build environment
// configuration variables).
+ //
// Note: could be quoted.
//
- strings config;
+ strings target_config;
+
+ // Whitespace separated list of potentially double/single-quoted package
+ // configuration arguments for bpkg-pkg-build command.
+ //
+ std::string package_config;
+
+ // If true, then this configuration is self-hosted.
+ //
+ butl::optional<bool> host;
// Regular expressions for detecting warnings in the operation result logs
// (in addition to the default list of expressions).
@@ -146,8 +273,12 @@ namespace bbot
//
strings warning_regex;
+ butl::optional<std::string> interactive; // Interactive build breakpoint.
+
+ butl::optional<std::string> worker_checksum;
+
strings
- unquoted_config () const;
+ unquoted_target_config () const;
strings
unquoted_warning_regex () const;
@@ -156,22 +287,38 @@ namespace bbot
bpkg::version vr,
bpkg::repository_location rl,
strings tr,
- butl::small_vector<package, 1> te,
+ std::vector<bpkg::requirement_alternatives> ra,
+ butl::small_vector<bpkg::test_dependency, 1> ts,
+ butl::optional<std::string> dc,
std::string mn,
+ std::vector<auxiliary_machine> ams,
butl::target_triplet tg,
butl::optional<std::string> en,
- strings cf,
- strings wr)
+ butl::optional<std::string> ae,
+ strings tc,
+ std::string pc,
+ butl::optional<bool> ht,
+ strings wr,
+ butl::optional<std::string> ir,
+ butl::optional<std::string> wc)
: name (std::move (nm)),
version (std::move (vr)),
repository (std::move (rl)),
trust (std::move (tr)),
- test_exclusions (std::move (te)),
+ requirements (std::move (ra)),
+ tests (std::move (ts)),
+ dependency_checksum (std::move (dc)),
machine (std::move (mn)),
+ auxiliary_machines (std::move (ams)),
target (std::move (tg)),
environment (std::move (en)),
- config (std::move (cf)),
- warning_regex (std::move (wr)){}
+ auxiliary_environment (std::move (ae)),
+ target_config (std::move (tc)),
+ package_config (std::move (pc)),
+ host (std::move (ht)),
+ warning_regex (std::move (wr)),
+ interactive (std::move (ir)),
+ worker_checksum (std::move (wc)) {}
public:
task_manifest () = default; // VC export.
@@ -190,6 +337,16 @@ namespace bbot
validate_regex (const std::string&);
};
+ class upload_url
+ {
+ public:
+ std::string url;
+ std::string type;
+
+ upload_url (std::string u, std::string t)
+ : url (std::move (u)), type (std::move (t)) {}
+ };
+
class LIBBBOT_EXPORT task_response_manifest
{
public:
@@ -197,19 +354,28 @@ namespace bbot
//
std::string session;
- // Challenge, result url and task are absent if session is empty.
+ // Challenge, result url, and task are absent and upload urls list is
+ // empty if session is empty.
//
butl::optional<std::string> challenge;
butl::optional<std::string> result_url;
+ std::vector<upload_url> upload_urls; // <type>-upload-url: <url>
+
+ butl::optional<std::string> agent_checksum;
+
butl::optional<task_manifest> task;
task_response_manifest (std::string s,
butl::optional<std::string> c,
butl::optional<std::string> u,
+ std::vector<upload_url> uus,
+ butl::optional<std::string> ac,
butl::optional<task_manifest> t)
: session (std::move (s)),
challenge (std::move (c)),
result_url (std::move (u)),
+ upload_urls (std::move (uus)),
+ agent_checksum (std::move (ac)),
task (std::move (t)) {}
public:
@@ -229,11 +395,13 @@ namespace bbot
// indicate whether one "overrides" the other in the "merge" operator|
// (see below).
//
+ skip,
success,
warning,
error,
abort,
- abnormal
+ abnormal,
+ interrupt
};
LIBBBOT_EXPORT std::string
@@ -292,14 +460,21 @@ namespace bbot
//
operation_results results;
+ butl::optional<std::string> worker_checksum;
+ butl::optional<std::string> dependency_checksum;
+
result_manifest (bpkg::package_name n,
bpkg::version v,
result_status s,
- operation_results r)
+ operation_results r,
+ butl::optional<std::string> wc,
+ butl::optional<std::string> dc)
: name (std::move (n)),
version (std::move (v)),
status (s),
- results (std::move (r)) {}
+ results (std::move (r)),
+ worker_checksum (std::move (wc)),
+ dependency_checksum (std::move (dc)) {}
public:
result_manifest () = default; // VC export.
@@ -321,13 +496,17 @@ namespace bbot
//
butl::optional<std::vector<char>> challenge;
+ std::string agent_checksum;
+
result_manifest result;
result_request_manifest (std::string s,
butl::optional<std::vector<char>> c,
+ std::string ac,
result_manifest r)
: session (std::move (s)),
challenge (std::move (c)),
+ agent_checksum (std::move (ac)),
result (std::move (r)) {}
public: