aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-04-06 19:46:03 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-04-06 21:20:37 +0300
commitcfd31379be5eefb22a72b5ee90ce8fd17a0802b7 (patch)
tree19fa3727e1799c98938681f79328b60d63afff19
parente3af4c881790a95be2676a7f4fb8df4136f8a3f3 (diff)
Implement manifests
-rw-r--r--bbot/bootstrap-manifest32
-rw-r--r--bbot/bootstrap-manifest.cxx260
-rw-r--r--bbot/buildfile2
-rw-r--r--bbot/utility3
-rw-r--r--build/root.build9
-rw-r--r--buildfile7
-rw-r--r--unit-tests/.gitignore1
-rw-r--r--unit-tests/bootstrap-manifest/buildfile11
-rw-r--r--unit-tests/bootstrap-manifest/driver.cxx62
-rw-r--r--unit-tests/bootstrap-manifest/testscript130
10 files changed, 509 insertions, 8 deletions
diff --git a/bbot/bootstrap-manifest b/bbot/bootstrap-manifest
index 502127b..c29406a 100644
--- a/bbot/bootstrap-manifest
+++ b/bbot/bootstrap-manifest
@@ -7,6 +7,8 @@
#include <map>
+#include <butl/manifest-forward>
+
#include <bbot/types>
#include <bbot/utility>
@@ -23,6 +25,16 @@ namespace bbot
// Toolchain id (SHAXXX).
//
string id;
+
+ public:
+ toolchain_manifest () = default; // VC export.
+ toolchain_manifest (butl::manifest_parser&, bool ignore_unknown = false);
+ toolchain_manifest (butl::manifest_parser&,
+ butl::manifest_name_value start,
+ bool ignore_unknown = false);
+
+ void
+ serialize (butl::manifest_serializer&) const;
};
// Bootstrap result manifest. Uploaded by the worker to the agent's TFTP
@@ -40,10 +52,20 @@ namespace bbot
// bbot-version: 1010200 # 1.1.2
//
std::map<string, uint64_t> versions;
+
+ public:
+ bootstrap_manifest () = default; // VC export.
+ bootstrap_manifest (butl::manifest_parser&, bool ignore_unknown = false);
+ bootstrap_manifest (butl::manifest_parser&,
+ butl::manifest_name_value start,
+ bool ignore_unknown = false);
+
+ void
+ serialize (butl::manifest_serializer&) const;
};
// The manifest stored in <name>-<toolchain>/ consists of the machine
- // manifest (original), toolchain manifest, and bootstrap result manifest.
+ // manifest (original), toolchain manifest, and bootstrap manifest.
//
class bootstrapped_machine_manifest
{
@@ -51,6 +73,14 @@ namespace bbot
machine_manifest machine;
toolchain_manifest toolchain;
bootstrap_manifest bootstrap;
+
+ public:
+ bootstrapped_machine_manifest () = default; // VC export.
+ bootstrapped_machine_manifest (butl::manifest_parser&,
+ bool ignore_unknown = false);
+
+ void
+ serialize (butl::manifest_serializer&) const;
};
}
diff --git a/bbot/bootstrap-manifest.cxx b/bbot/bootstrap-manifest.cxx
new file mode 100644
index 0000000..7d635ca
--- /dev/null
+++ b/bbot/bootstrap-manifest.cxx
@@ -0,0 +1,260 @@
+// file : bbot/bootstrap-manifest.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bbot/bootstrap-manifest>
+
+#include <butl/manifest-parser>
+#include <butl/manifest-serializer>
+
+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;
+
+ // 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.
+ }
+
+ // bootstrap_manifest
+ //
+ bootstrap_manifest::
+ bootstrap_manifest (parser& p, bool iu)
+ : bootstrap_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 bootstrap manifest expected");
+ }
+
+ bootstrap_manifest::
+ bootstrap_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 bootstrap manifest expected");
+
+ if (nv.value != "1")
+ bad_value ("unsupported format version");
+
+ // Parse the bootstrap manifest.
+ //
+ for (nv = p.next (); !nv.empty (); nv = p.next ())
+ {
+ string& n (nv.name);
+ string& v (nv.value);
+
+ size_t nn (n.size ()); // Name length.
+
+ // Note: returns false if nothing preceeds a suffix.
+ //
+ auto suffix = [&n, nn] (const char* s, size_t sn) -> bool
+ {
+ return nn > sn && n.compare (nn - sn, sn, s) == 0;
+ };
+
+ size_t sn;
+ if (suffix ("-version", sn = 8))
+ {
+ string pn (n, 0, nn - sn); // Package name.
+
+ // Package version.
+ //
+ size_t vn;
+ uint64_t pv (stoull (v, &vn));
+ if (vn != v.size ())
+ bad_value ("invalid package version");
+
+ // Make sure the package version is not redefined.
+ //
+ if (!versions.emplace (move (pn), pv).second)
+ bad_name (n + " redefinition");
+ }
+ else if (!iu)
+ bad_name ("unknown name '" + n + "' in bootstrap manifest");
+ }
+
+ // Verify all non-optional values were specified.
+ //
+ if (versions.empty ())
+ bad_value ("no package versions specified");
+ }
+
+ void bootstrap_manifest::
+ serialize (serializer& s) const
+ {
+ // @@ Should we check that all non-optional values are specified?
+ //
+ s.next ("", "1"); // Start of manifest.
+
+ // Serialize *-version values.
+ //
+ for (const auto& v: versions)
+ s.next (v.first + "-version", to_string (v.second));
+
+ 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, false, iu);
+
+ 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.
+
+ machine.serialize (s);
+ toolchain.serialize (s);
+ bootstrap.serialize (s);
+
+ s.next ("", ""); // End of stream.
+ }
+}
diff --git a/bbot/buildfile b/bbot/buildfile
index c2d5d05..4045888 100644
--- a/bbot/buildfile
+++ b/bbot/buildfile
@@ -26,7 +26,7 @@ if ($cxx.target.class == "linux")
exe{bbot-agent}: \
{ cxx}{ agent } {hxx ixx cxx}{ agent-options } \
- {hxx ixx cxx}{ common-options } \
+ {hxx cxx}{ bootstrap-manifest } {hxx ixx cxx}{ common-options } \
{hxx cxx}{ diagnostics } \
{hxx }{ types } \
{hxx cxx}{ types-parsers } \
diff --git a/bbot/utility b/bbot/utility
index 032ffca..81c6c87 100644
--- a/bbot/utility
+++ b/bbot/utility
@@ -6,7 +6,7 @@
#define BBOT_UTILITY
#include <memory> // make_shared()
-#include <string> // to_string()
+#include <string> // to_string(), stoull()
#include <utility> // move(), forward(), declval(), make_pair()
#include <cassert> // assert()
#include <iterator> // make_move_iterator()
@@ -30,6 +30,7 @@ namespace bbot
using std::make_shared;
using std::make_move_iterator;
using std::to_string;
+ using std::stoull;
// <butl/utility>
//
diff --git a/build/root.build b/build/root.build
index f19d700..e36af49 100644
--- a/build/root.build
+++ b/build/root.build
@@ -21,6 +21,11 @@ cxx.poptions =+ "-I$out_root" "-I$src_root"
#
using? cli
-# All exe{} in tests/ are, well, tests.
+# All exe{} in tests/ and unit-tests/ are, well, tests.
#
-tests/exe{*}: test = true
+tests/exe{*}: test = true
+unit-tests/exe{*}: test = true
+
+# Specify the test target for cross-testing.
+#
+test.target = $cxx.target
diff --git a/buildfile b/buildfile
index b825c0a..513c4aa 100644
--- a/buildfile
+++ b/buildfile
@@ -2,9 +2,10 @@
# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
-./: bbot/ doc{INSTALL LICENSE NEWS README version} file{manifest}
+./: bbot/ unit-tests/ doc{INSTALL LICENSE NEWS README version} file{manifest}
# Don't install tests or the INSTALL file.
#
-dir{tests/}: install = false
-doc{INSTALL}@./: install = false
+dir{tests/}: install = false
+dir{unit-tests/}: install = false
+doc{INSTALL}@./: install = false
diff --git a/unit-tests/.gitignore b/unit-tests/.gitignore
new file mode 100644
index 0000000..e54525b
--- /dev/null
+++ b/unit-tests/.gitignore
@@ -0,0 +1 @@
+driver
diff --git a/unit-tests/bootstrap-manifest/buildfile b/unit-tests/bootstrap-manifest/buildfile
new file mode 100644
index 0000000..aa598ee
--- /dev/null
+++ b/unit-tests/bootstrap-manifest/buildfile
@@ -0,0 +1,11 @@
+# file : unit-tests/bootstrap-manifest/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs = libbutl%lib{butl}
+import libs += libbbot%lib{bbot}
+
+exe{driver}: cxx{driver} ../../bbot/cxx{bootstrap-manifest} $libs \
+ test{testscript}
+
+include ../../bbot/
diff --git a/unit-tests/bootstrap-manifest/driver.cxx b/unit-tests/bootstrap-manifest/driver.cxx
new file mode 100644
index 0000000..3531383
--- /dev/null
+++ b/unit-tests/bootstrap-manifest/driver.cxx
@@ -0,0 +1,62 @@
+// file : unit-tests/bootstrap-manifest/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <ios> // ios_base::failbit, ios_base::badbit
+#include <iostream>
+
+#include <butl/manifest-parser>
+#include <butl/manifest-serializer>
+
+#include <bbot/types>
+#include <bbot/utility>
+
+#include <bbot/bootstrap-manifest>
+
+using namespace std;
+using namespace butl;
+using namespace bbot;
+
+// Usage: argv[0] (-t|-b|-bm)
+//
+// Read and parse manifest from STDIN and serialize it to STDOUT. The
+// following options specify the manifest type.
+//
+// -t parse toolchain manifest
+// -b parse bootstrap manifest
+// -bm parse bootstrapped machine manifest
+//
+int
+main (int argc, char* argv[])
+try
+{
+ assert (argc == 2);
+ string opt (argv[1]);
+
+ cin.exceptions (ios_base::failbit | ios_base::badbit);
+ cout.exceptions (ios_base::failbit | ios_base::badbit);
+
+ manifest_parser p (cin, "stdin");
+ manifest_serializer s (cout, "stdout");
+
+ if (opt == "-t")
+ toolchain_manifest (p).serialize (s);
+ else if (opt == "-b")
+ bootstrap_manifest (p).serialize (s);
+ else if (opt == "-bm")
+ bootstrapped_machine_manifest (p).serialize (s);
+ else
+ assert (false);
+
+ return 0;
+}
+catch (const manifest_parsing& e)
+{
+ cerr << e << endl;
+ return 1;
+}
+catch (const manifest_serialization& e)
+{
+ cerr << e << endl;
+ return 1;
+}
diff --git a/unit-tests/bootstrap-manifest/testscript b/unit-tests/bootstrap-manifest/testscript
new file mode 100644
index 0000000..2a104be
--- /dev/null
+++ b/unit-tests/bootstrap-manifest/testscript
@@ -0,0 +1,130 @@
+# file : unit-tests/bootstrap-manifest/testscript
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+: toolchain-manifest
+:
+{
+ test.options += -t
+
+ : valid
+ :
+ $* <<EOF >>EOF
+ : 1
+ id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ EOF
+
+ : no-id
+ :
+ $* <<EOI 2>'stdin:2:1: error: no toolchain id specified' == 1
+ : 1
+ EOI
+
+ : unknown
+ :
+ $* <<EOI 2>"stdin:2:1: error: unknown name 'x' in toolchain manifest" == 1
+ : 1
+ x: y
+ EOI
+}
+
+: bootstrap-manifest
+:
+{
+ test.options += -b
+
+ : valid
+ :
+ $* <<EOF >>EOF
+ : 1
+ bbot-version: 1010200
+ libbbot-version: 1010100
+ EOF
+
+ : dup
+ :
+ $* <<EOI 2>'stdin:3:1: error: bbot-version redefinition' == 1
+ : 1
+ bbot-version: 1010200
+ bbot-version: 1010200
+ EOI
+
+ : invalid-version
+ :
+ $* <<EOI 2>'stdin:2:15: error: invalid package version' == 1
+ : 1
+ bbot-version: 1010200A
+ EOI
+
+ : no-versions
+ :
+ $* <<EOI 2>'stdin:2:1: error: no package versions specified' == 1
+ : 1
+ EOI
+
+ : unknown
+ :
+ $* <<EOI 2>"stdin:2:1: error: unknown name 'x' in bootstrap manifest" == 1
+ : 1
+ x: y
+ EOI
+}
+
+: bootstrapped-machine-manifest
+:
+{
+ test.options += -bm
+
+ : valid
+ :
+ $* <<EOF >>EOF
+ : 1
+ :
+ id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ name: windows_10-msvc_14
+ summary: Windows 10 build 1607 with VC 14 update 3
+ type: kvm
+ :
+ id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ :
+ bbot-version: 1010200
+ libbbot-version: 1010100
+ EOF
+
+ : unknown
+ :
+ $* <<EOI 2>"stdin:2:1: error: unknown name 'x' in bootstrapped machine manifest" == 1
+ : 1
+ x: y
+ EOI
+
+ : no-machine
+ :
+ $* <<EOI 2>'stdin:2:1: error: machine manifest expected' == 1
+ : 1
+ EOI
+
+ : no-toolchain
+ :
+ $* <<EOI 2>'stdin:7:1: error: toolchain manifest expected' == 1
+ : 1
+ :
+ id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ name: windows_10-msvc_14
+ summary: Windows 10 build 1607 with VC 14 update 3
+ type: kvm
+ EOI
+
+ : no-bootstrap
+ :
+ $* <<EOI 2>'stdin:9:1: error: bootstrap manifest expected' == 1
+ : 1
+ :
+ id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ name: windows_10-msvc_14
+ summary: Windows 10 build 1607 with VC 14 update 3
+ type: kvm
+ :
+ id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ EOI
+}