From b1078fdb9fac747c19dbdacd24c2838aae7d9f6b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 7 Sep 2015 15:59:37 +0200 Subject: Implement cfg-create command --- bpkg/bpkg-options.cli | 7 +++ bpkg/bpkg.cxx | 15 ++++- bpkg/buildfile | 18 ++++-- bpkg/cfg-create | 17 +++++ bpkg/cfg-create-options.cli | 41 ++++++++++++ bpkg/cfg-create.cxx | 148 ++++++++++++++++++++++++++++++++++++++++++++ bpkg/common-options.cli | 1 + bpkg/diagnostics | 6 +- bpkg/diagnostics.cxx | 6 +- bpkg/types-parsers | 31 ++++++++++ bpkg/types-parsers.cxx | 37 +++++++++++ bpkg/utility | 22 +++++++ bpkg/utility.cxx | 80 ++++++++++++++++++++++++ 13 files changed, 416 insertions(+), 13 deletions(-) create mode 100644 bpkg/cfg-create create mode 100644 bpkg/cfg-create-options.cli create mode 100644 bpkg/cfg-create.cxx create mode 100644 bpkg/types-parsers create mode 100644 bpkg/types-parsers.cxx (limited to 'bpkg') diff --git a/bpkg/bpkg-options.cli b/bpkg/bpkg-options.cli index a9d373f..d8dc945 100644 --- a/bpkg/bpkg-options.cli +++ b/bpkg/bpkg-options.cli @@ -21,6 +21,13 @@ namespace bpkg "" }; + bool cfg-create + { + "[]", + "Create configuration.", + "" + }; + bool rep-create { "[]", diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx index 8811ba8..eeafa09 100644 --- a/bpkg/bpkg.cxx +++ b/bpkg/bpkg.cxx @@ -14,6 +14,7 @@ // Commands. // #include +#include #include using namespace std; @@ -35,7 +36,7 @@ parse (const common_options& co, cli::scanner& s) // Global initializations. // - // Trace verbosity. + // Diagnostics verbosity. // verb = o.verbose_specified () ? o.verbose () : o.v () ? 2 : o.q () ? 0 : 1; @@ -134,6 +135,18 @@ try return 0; } + // cfg-create + // + if (cmd.cfg_create ()) + { + if (h) + help (ho, "cfg-create", cfg_create_options::print_usage); + else + cfg_create (parse (co, args), args); + + return 0; + } + // rep-create // if (cmd.rep_create ()) diff --git a/bpkg/buildfile b/bpkg/buildfile index 0e03e93..e058957 100644 --- a/bpkg/buildfile +++ b/bpkg/buildfile @@ -7,15 +7,20 @@ using cli import libs = libbutl%lib{butl} import libs += libbpkg%lib{bpkg} -exe{bpkg}: cxx{diagnostics utility} cli.cxx{common-options} \ - cxx{bpkg} cli.cxx{bpkg-options} \ - cxx{help} cli.cxx{help-options} \ - cxx{rep-create} cli.cxx{rep-create-options} \ +exe{bpkg}: cxx{diagnostics utility} \ + cli.cxx{common-options} cxx{types-parsers} \ + cxx{bpkg} cli.cxx{bpkg-options} \ + cxx{help} cli.cxx{help-options} \ + cxx{cfg-create} cli.cxx{cfg-create-options} \ + cxx{rep-create} cli.cxx{rep-create-options} \ $libs +#@@ --cxx-prologue "#include " + cli.options += -I $src_root --include-with-brackets --include-prefix bpkg \ ---guard-prefix BPKG --generate-file-scanner --cli-namespace bpkg::cli \ ---generate-specifier --generate-parse --long-usage --exclude-base +--guard-prefix BPKG --cxx-prologue-file $src_base/types-parsers \ +--cli-namespace bpkg::cli --generate-file-scanner --generate-specifier \ +--generate-parse --long-usage --exclude-base # Option length must be the same to get commands/topics/options lists # aligned in the general help. @@ -27,4 +32,5 @@ cli.cxx{bpkg-options}: cli{bpkg-options} cli.cxx{bpkg-options}: cli.options += --option-length 22 --short-usage cli.cxx{help-options}: cli{help-options} +cli.cxx{cfg-create-options}: cli{cfg-create-options} cli.cxx{rep-create-options}: cli{rep-create-options} diff --git a/bpkg/cfg-create b/bpkg/cfg-create new file mode 100644 index 0000000..ffd3ba5 --- /dev/null +++ b/bpkg/cfg-create @@ -0,0 +1,17 @@ +// file : bpkg/cfg-create -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BPKG_CFG_CREATE +#define BPKG_CFG_CREATE + +#include +#include + +namespace bpkg +{ + void + cfg_create (const cfg_create_options&, cli::scanner& args); +} + +#endif // BPKG_CFG_CREATE diff --git a/bpkg/cfg-create-options.cli b/bpkg/cfg-create-options.cli new file mode 100644 index 0000000..24bc102 --- /dev/null +++ b/bpkg/cfg-create-options.cli @@ -0,0 +1,41 @@ +// file : bpkg/cfg-create-options.cli +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +include ; + +/* +"\section=1" +"\name=bpkg-cfg-create" + +"\h{SYNOPSIS} + +bpkg cfg-create [] [(|)...]" + +"\h{DESCRIPTION} + +The \cb{cfg-create} command creates a new \cb{bpkg} configuration with +the specified \cb{build2} modules and configuration variables. Unless +the \cb{--wipe} option is specified, \cb{cfg-create} expects the +configuration directory to not exist (in which case it will be created) +or to be empty." +*/ + +namespace bpkg +{ + class cfg_create_options: common_options + { + dir_path --directory | -d (".") + { + "", + "Create configuration in rather than in the current working + directory." + }; + + bool --wipe + { + "Wipe the configuration directory clean before creating the new + configuration." + }; + }; +} diff --git a/bpkg/cfg-create.cxx b/bpkg/cfg-create.cxx new file mode 100644 index 0000000..4fa5de3 --- /dev/null +++ b/bpkg/cfg-create.cxx @@ -0,0 +1,148 @@ +// file : bpkg/cfg-create.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include // move() +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace butl; + +namespace bpkg +{ + void + cfg_create (const cfg_create_options& o, cli::scanner& args) + { + tracer trace ("cfg_create"); + + dir_path d (o.directory ()); + level4 ([&]{trace << "creating configuration in " << d;}); + + // If the directory already exists, make sure it is empty. + // Otherwise, create it. + // + if (exists (d)) + { + if (!empty (d)) + { + if (!o.wipe ()) + fail << "directory " << d << " is not empty"; + + rm_r (d, false); + } + } + else + mk_p (d); + + // Sort arguments into modules and configuration variables. + // + strings mods; + strings vars; + + while (args.more ()) + { + string a (args.next ()); + (a.find ('=') != string::npos ? vars : mods).push_back (move (a)); + } + + // Create build/. + // + dir_path bd (d / dir_path ("build")); + mk (bd); + + // Write build/bootstrap.build. + // + path f (bd / path ("bootstrap.build")); + try + { + ofstream ofs; + ofs.exceptions (ofstream::badbit | ofstream::failbit); + ofs.open (f.string ()); + + ofs << "# Maintained automatically by bpkg, do not edit." << endl + << "#" << endl + << "project =" << endl + << "amalgamation =" << endl + << "using config" << endl + << "using install" << endl; + + // Load user-supplied modules in bootstrap.build instead of root.build + // since we don't know whether they can be loaded in the latter. + // + for (const string& m: mods) + ofs << "using " << m << endl; + } + catch (const ofstream::failure&) + { + fail << "unable to write to " << f; + } + + // Write root buildfile. + // + f = path (d / path ("buildfile")); + try + { + ofstream ofs; + ofs.exceptions (ofstream::badbit | ofstream::failbit); + ofs.open (f.string ()); + + ofs << "# Maintained automatically by bpkg, do not edit." << endl + << "#" << endl + << "./:" << endl; + } + catch (const ofstream::failure&) + { + fail << "unable to write to " << f; + } + + // Configure. + // + { + cstrings args {"b"}; + + // Map verbosity level. If we are running quiet or at level 1, + // then run build2 quiet. Otherwise, run it at the same level + // as us. + // + string vl; + if (verb <= 1) + args.push_back ("-q"); + else if (verb == 2) + args.push_back ("-v"); + else + { + vl = to_string (verb); + args.push_back ("--verbose"); + args.push_back (vl.c_str ()); + } + + // Add config vars. + // + for (const string& v: vars) + args.push_back (v.c_str ()); + + // Add buildspec. + // + string bspec ("configure(" + d.string () + "/)"); + args.push_back (bspec.c_str ()); + + args.push_back (nullptr); + run (args); + } + + if (verb) + { + d.complete (); + d.normalize (); + text << "created new configuration in " << d; + } + } +} diff --git a/bpkg/common-options.cli b/bpkg/common-options.cli index a8b36de..2532665 100644 --- a/bpkg/common-options.cli +++ b/bpkg/common-options.cli @@ -3,6 +3,7 @@ // license : MIT; see accompanying LICENSE file include ; +include ; namespace bpkg { diff --git a/bpkg/diagnostics b/bpkg/diagnostics index b672282..2d7825a 100644 --- a/bpkg/diagnostics +++ b/bpkg/diagnostics @@ -35,10 +35,10 @@ namespace bpkg // nameN arg arg ... nullptr nullptr // void - print_process (diag_record&, const char* const* args, std::size_t n = 0); + print_process (diag_record&, const char* const args[], std::size_t n = 0); void - print_process (const char* const* args, std::size_t n = 0); + print_process (const char* const args[], std::size_t n = 0); inline void print_process (diag_record& dr, const cstrings& args) @@ -55,7 +55,7 @@ namespace bpkg // Verbosity level. // // 0 - disabled - // 1 - general information messages + // 1 - high-level information messages // 2 - underlying commands being executed // 3 - information that could be helpful to the user // 4 - information that could be helpful to the developer diff --git a/bpkg/diagnostics.cxx b/bpkg/diagnostics.cxx index 4c5e24b..39ad1d2 100644 --- a/bpkg/diagnostics.cxx +++ b/bpkg/diagnostics.cxx @@ -13,14 +13,14 @@ namespace bpkg // print_process // void - print_process (const char* const* args, size_t n) + print_process (const char* const args[], size_t n) { diag_record r (text); print_process (r, args, n); } void - print_process (diag_record& r, const char* const* args, size_t n) + print_process (diag_record& r, const char* const args[], size_t n) { size_t m (0); const char* const* p (args); @@ -44,7 +44,7 @@ namespace bpkg } while (*p != nullptr); } - // Trace verbosity level. + // Diagnostics verbosity level. // uint16_t verb; diff --git a/bpkg/types-parsers b/bpkg/types-parsers new file mode 100644 index 0000000..8aacd09 --- /dev/null +++ b/bpkg/types-parsers @@ -0,0 +1,31 @@ +// file : bpkg/types-parsers -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +// CLI parsers, included into the generated source files. +// + +#ifndef BPKG_TYPES_PARSERS +#define BPKG_TYPES_PARSERS + +#include + +namespace bpkg +{ + namespace cli + { + class scanner; + + template + struct parser; + + template <> + struct parser + { + static void + parse (dir_path&, bool&, scanner&); + }; + } +} + +#endif // BPKG_TYPES_PARSERS diff --git a/bpkg/types-parsers.cxx b/bpkg/types-parsers.cxx new file mode 100644 index 0000000..544908c --- /dev/null +++ b/bpkg/types-parsers.cxx @@ -0,0 +1,37 @@ +// file : bpkg/types-parsers.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include // bpkg::cli namespace + +namespace bpkg +{ + namespace cli + { + void parser:: + parse (dir_path& x, bool& xs, scanner& s) + { + xs = true; + const char* o (s.next ()); + + if (!s.more ()) + throw missing_value (o); + + const char* v (s.next ()); + + try + { + x = dir_path (v); + + if (x.empty ()) + throw invalid_value (o, v); + } + catch (const invalid_path&) + { + throw invalid_value (o, v); + } + } + } +} diff --git a/bpkg/utility b/bpkg/utility index 9c6688f..f2c0ab5 100644 --- a/bpkg/utility +++ b/bpkg/utility @@ -9,11 +9,33 @@ namespace bpkg { + // Filesystem. + // bool exists (const path&); bool exists (const dir_path&); + + bool + empty (const dir_path&); + + void + mk (const dir_path&); + + void + mk_p (const dir_path&); + + void + rm_r (const dir_path&, bool dir = true); + + // Process. + // + void + run (const char* const args[]); + + inline void + run (const cstrings& args) {run (args.data ());} } #endif // BPKG_UTILITY diff --git a/bpkg/utility.cxx b/bpkg/utility.cxx index c274563..1cd518b 100644 --- a/bpkg/utility.cxx +++ b/bpkg/utility.cxx @@ -6,6 +6,7 @@ #include +#include #include #include @@ -43,4 +44,83 @@ namespace bpkg throw failed (); } } + + bool + empty (const dir_path& d) + { + try + { + dir_iterator i (d); + return i == end (i); + } + catch (const system_error& e) + { + error << "unable to scan directory " << d << ": " << e.what (); + throw failed (); + } + } + + void + mk (const dir_path& d) + { + try + { + try_mkdir (d); + } + catch (const system_error& e) + { + fail << "unable to create directory " << d << ": " << e.what (); + } + } + + void + mk_p (const dir_path& d) + { + try + { + try_mkdir_p (d); + } + catch (const system_error& e) + { + fail << "unable to create directory " << d << ": " << e.what (); + } + } + + void + rm_r (const dir_path& d, bool dir) + { + try + { + rmdir_r (d, dir); + } + catch (const system_error& e) + { + fail << "unable to remove " << (dir ? "" : "contents of ") + << "directory " << d << ": " << e.what (); + } + } + + void + run (const char* const args[]) + { + if (verb >= 2) + print_process (args); + + try + { + process pr (args); + + if (!pr.wait ()) + throw failed (); // Assume the child issued diagnostics. + } + catch (const process_error& e) + { + error << "unable to execute " << args[0] << ": " << e.what (); + + if (e.child ()) + exit (1); + + throw failed (); + } + } } -- cgit v1.1