From a69f728a710bcc4e17913a57ffb01da076467bfb Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 29 Jul 2017 10:54:31 +0200 Subject: Convert to use utility library --- bbot/machine-manifest.cxx | 355 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 bbot/machine-manifest.cxx (limited to 'bbot/machine-manifest.cxx') diff --git a/bbot/machine-manifest.cxx b/bbot/machine-manifest.cxx new file mode 100644 index 0000000..b7baf7e --- /dev/null +++ b/bbot/machine-manifest.cxx @@ -0,0 +1,355 @@ +// file : bbot/machine-manifest.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : TBC; see accompanying LICENSE file + +#include + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace butl; + +namespace bbot +{ + using parser = manifest_parser; + using parsing = manifest_parsing; + using serializer = manifest_serializer; + using serialization = manifest_serialization; + using name_value = manifest_name_value; + + // machine_type + // + string + to_string (machine_type t) + { + switch (t) + { + case machine_type::kvm: return "kvm"; + case machine_type::nspawn: return "nspawn"; + } + + assert (false); + return string (); + } + + machine_type + to_machine_type (const string& t) + { + if (t == "kvm") return machine_type::kvm; + else if (t == "nspawn") return machine_type::nspawn; + else throw invalid_argument ("invalid machine type '" + t + "'"); + } + + // machine_manifest + // + machine_manifest:: + machine_manifest (parser& p, bool iu) + : machine_manifest (p, p.next (), iu) + { + // Make sure this is the end. + // + name_value nv (p.next ()); + if (!nv.empty ()) + throw parsing (p.name (), nv.name_line, nv.name_column, + "single machine manifest expected"); + } + + machine_manifest:: + machine_manifest (parser& p, name_value nv, bool iu) + : machine_header_manifest (p, move (nv), unknown_name_mode::stop, &nv) + { + auto bad_name = [&p, &nv] (const string& d) + { + throw parsing (p.name (), nv.name_line, nv.name_column, d); + }; + + // Offsets are used to tie an error to the specific position inside a + // manifest value (possibly a multiline one). + // + auto bad_value = [&p, &nv] ( + const string& d, uint64_t column_offset = 0, uint64_t line_offset = 0) + { + throw parsing (p.name (), + nv.value_line + line_offset, + (line_offset == 0 ? nv.value_column : 1) + column_offset, + d); + }; + + optional type; + + for (; !nv.empty (); nv = p.next ()) + { + string& n (nv.name); + string& v (nv.value); + + if (n == "type") + { + if (type) + bad_name ("machine type redefinition"); + + try + { + type = to_machine_type (v); + } + catch (const invalid_argument&) + { + bad_value ("invalid machine type"); + } + } + else if (n == "mac") + { + if (mac) + bad_name ("machine mac redefinition"); + + // @@ Should we check that the value is a valid mac? + // + mac = move (v); + } + else if (n == "options") + { + if (options) + bad_name ("machine options redefinition"); + + strings op; + + // Note that when reporting errors we combine the manifest value + // position with the respective error position. + // + try + { + istringstream is (v); + tab_parser parser (is, ""); + + tab_fields tl; + while (!(tl = parser.next ()).empty ()) + { + for (auto& tf: tl) + op.emplace_back (move (tf.value)); + } + } + catch (const tab_parsing& e) + { + bad_value ("invalid machine options: " + e.description, + e.column - 1, + e.line - 1); + } + + if (op.empty ()) + bad_value ("empty machine options"); + + options = move (op); + } + else if (!iu) + bad_name ("unknown name '" + n + "' in machine manifest"); + } + + // Verify all non-optional values were specified. + // + if (!type) + bad_value ("no machine type specified"); + + this->type = *type; + } + + void machine_manifest:: + serialize (serializer& s) const + { + // @@ Should we check that all non-optional values are specified and all + // values are valid? + // + + machine_header_manifest::serialize (s, false); + + s.next ("type", to_string (type)); + + if (mac) + s.next ("mac", *mac); + + // Recompose options string as a space-separated option list, + // + if (options) + { + string v; + for (auto b (options->cbegin ()), i (b), e (options->cend ()); i != e; + ++i) + { + if (i != b) + v += ' '; + + v += *i; + } + + s.next ("options", v); + } + + s.next ("", ""); // End of manifest. + } + + strings machine_manifest:: + unquoted_options () const + { + return options + ? string_parser::unquote (*options) + : strings (); + } + + // toolchain_manifest + // + toolchain_manifest:: + toolchain_manifest (parser& p, bool iu) + : toolchain_manifest (p, p.next (), iu) + { + // Make sure this is the end. + // + name_value nv (p.next ()); + if (!nv.empty ()) + throw parsing (p.name (), nv.name_line, nv.name_column, + "single toolchain manifest expected"); + } + + toolchain_manifest:: + toolchain_manifest (parser& p, name_value nv, bool iu) + { + auto bad_name = [&p, &nv] (const string& d) + { + throw parsing (p.name (), nv.name_line, nv.name_column, d); + }; + + auto bad_value = [&p, &nv] (const string& d) + { + throw parsing (p.name (), nv.value_line, nv.value_column, d); + }; + + // Make sure this is the start and we support the version. + // + if (!nv.name.empty ()) + bad_name ("start of toolchain manifest expected"); + + if (nv.value != "1") + bad_value ("unsupported format version"); + + // Parse the toolchain manifest. + // + for (nv = p.next (); !nv.empty (); nv = p.next ()) + { + string& n (nv.name); + string& v (nv.value); + + if (n == "id") + { + if (!id.empty ()) + bad_name ("toolchain id redefinition"); + + if (v.empty ()) + bad_value ("empty toolchain id"); + + id = move (v); + } + else if (!iu) + bad_name ("unknown name '" + n + "' in toolchain manifest"); + } + + // Verify all non-optional values were specified. + // + if (id.empty ()) + bad_value ("no toolchain id specified"); + } + + void toolchain_manifest:: + serialize (serializer& s) const + { + // @@ Should we check that all non-optional values are specified? + // + s.next ("", "1"); // Start of manifest. + s.next ("id", id); + s.next ("", ""); // End of manifest. + } + + // bootstrapped_machine_manifest + // + bootstrapped_machine_manifest:: + bootstrapped_machine_manifest (parser& p, bool iu) + { + name_value nv (p.next ()); + + auto bad_name = [&p, &nv] (const string& d) + { + throw parsing (p.name (), nv.name_line, nv.name_column, d); + }; + + auto bad_value = [&p, &nv] (const string& d) + { + throw parsing (p.name (), nv.value_line, nv.value_column, d); + }; + + // Make sure this is the start and we support the version. + // + if (!nv.name.empty ()) + bad_name ("start of bootstrapped machine manifest expected"); + + if (nv.value != "1") + bad_value ("unsupported format version"); + + // Parse the bootstrapped machine manifest. Currently there is no values + // expected. + // + for (nv = p.next (); !nv.empty (); nv = p.next ()) + { + if (!iu) + bad_name ("unknown name '" + nv.name + + "' in bootstrapped machine manifest"); + } + + nv = p.next (); + if (nv.empty ()) + bad_value ("machine manifest expected"); + + machine = machine_manifest (p, nv, iu); + + if (!machine.mac) + bad_name ("mac address must be present in machine manifest"); + + nv = p.next (); + if (nv.empty ()) + bad_value ("toolchain manifest expected"); + + toolchain = toolchain_manifest (p, nv, iu); + + nv = p.next (); + if (nv.empty ()) + bad_value ("bootstrap manifest expected"); + + bootstrap = bootstrap_manifest (p, nv, iu); + + // Make sure this is the end. + // + nv = p.next (); + if (!nv.empty ()) + throw parsing (p.name (), nv.name_line, nv.name_column, + "single bootstrapped machine manifest expected"); + } + + void bootstrapped_machine_manifest:: + serialize (serializer& s) const + { + // @@ Should we check that all non-optional values are specified? + // + s.next ("", "1"); // Start of manifest. + s.next ("", ""); // End of manifest. + + if (!machine.mac) + throw serialization (s.name (), + "mac address must be present in machine manifest"); + + machine.serialize (s); + toolchain.serialize (s); + bootstrap.serialize (s); + + s.next ("", ""); // End of stream. + } +} -- cgit v1.1