diff options
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.cxx | 499 | ||||
-rw-r--r-- | libbbot/manifest.hxx | 241 |
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: |