aboutsummaryrefslogtreecommitdiff
path: root/bbot/machine-manifest.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-07-29 10:54:31 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-07-29 10:54:31 +0200
commita69f728a710bcc4e17913a57ffb01da076467bfb (patch)
treeb881854f9419975d87fdaf3b2f51db6492c97edf /bbot/machine-manifest.cxx
parentf179ab72a8c623383f68eac3e30635700d88dde9 (diff)
Convert to use utility library
Diffstat (limited to 'bbot/machine-manifest.cxx')
-rw-r--r--bbot/machine-manifest.cxx355
1 files changed, 355 insertions, 0 deletions
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 <bbot/machine-manifest.hxx>
+
+#include <sstream>
+
+#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;
+
+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<machine_type> 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.
+ }
+}