From cfd31379be5eefb22a72b5ee90ce8fd17a0802b7 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 6 Apr 2017 19:46:03 +0300 Subject: Implement manifests --- bbot/bootstrap-manifest | 32 +++- bbot/bootstrap-manifest.cxx | 260 +++++++++++++++++++++++++++++++ bbot/buildfile | 2 +- bbot/utility | 3 +- build/root.build | 9 +- buildfile | 7 +- unit-tests/.gitignore | 1 + unit-tests/bootstrap-manifest/buildfile | 11 ++ unit-tests/bootstrap-manifest/driver.cxx | 62 ++++++++ unit-tests/bootstrap-manifest/testscript | 130 ++++++++++++++++ 10 files changed, 509 insertions(+), 8 deletions(-) create mode 100644 bbot/bootstrap-manifest.cxx create mode 100644 unit-tests/.gitignore create mode 100644 unit-tests/bootstrap-manifest/buildfile create mode 100644 unit-tests/bootstrap-manifest/driver.cxx create mode 100644 unit-tests/bootstrap-manifest/testscript 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 +#include + #include #include @@ -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 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 -/ 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 + +#include +#include + +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 // make_shared() -#include // to_string() +#include // to_string(), stoull() #include // move(), forward(), declval(), make_pair() #include // assert() #include // 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; // // 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_base::failbit, ios_base::badbit +#include + +#include +#include + +#include +#include + +#include + +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 + : 1 + id: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 + EOF + + : no-id + : + $* <'stdin:2:1: error: no toolchain id specified' == 1 + : 1 + EOI + + : unknown + : + $* <"stdin:2:1: error: unknown name 'x' in toolchain manifest" == 1 + : 1 + x: y + EOI +} + +: bootstrap-manifest +: +{ + test.options += -b + + : valid + : + $* <>EOF + : 1 + bbot-version: 1010200 + libbbot-version: 1010100 + EOF + + : dup + : + $* <'stdin:3:1: error: bbot-version redefinition' == 1 + : 1 + bbot-version: 1010200 + bbot-version: 1010200 + EOI + + : invalid-version + : + $* <'stdin:2:15: error: invalid package version' == 1 + : 1 + bbot-version: 1010200A + EOI + + : no-versions + : + $* <'stdin:2:1: error: no package versions specified' == 1 + : 1 + EOI + + : unknown + : + $* <"stdin:2:1: error: unknown name 'x' in bootstrap manifest" == 1 + : 1 + x: y + EOI +} + +: bootstrapped-machine-manifest +: +{ + test.options += -bm + + : valid + : + $* <>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 + : + $* <"stdin:2:1: error: unknown name 'x' in bootstrapped machine manifest" == 1 + : 1 + x: y + EOI + + : no-machine + : + $* <'stdin:2:1: error: machine manifest expected' == 1 + : 1 + EOI + + : no-toolchain + : + $* <'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 + : + $* <'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 +} -- cgit v1.1