aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-09-07 15:59:37 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-09-07 15:59:37 +0200
commitb1078fdb9fac747c19dbdacd24c2838aae7d9f6b (patch)
tree3452bab97d91b063ac3cac2cb3d84ae184c21858
parent8f94aaa067426a259f5396abdaf4945671799b5c (diff)
Implement cfg-create command
-rw-r--r--bpkg/bpkg-options.cli7
-rw-r--r--bpkg/bpkg.cxx15
-rw-r--r--bpkg/buildfile18
-rw-r--r--bpkg/cfg-create17
-rw-r--r--bpkg/cfg-create-options.cli41
-rw-r--r--bpkg/cfg-create.cxx148
-rw-r--r--bpkg/common-options.cli1
-rw-r--r--bpkg/diagnostics6
-rw-r--r--bpkg/diagnostics.cxx6
-rw-r--r--bpkg/types-parsers31
-rw-r--r--bpkg/types-parsers.cxx37
-rw-r--r--bpkg/utility22
-rw-r--r--bpkg/utility.cxx80
13 files changed, 416 insertions, 13 deletions
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
+ {
+ "[<conf>]",
+ "Create configuration.",
+ ""
+ };
+
bool rep-create
{
"[<dir>]",
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 <bpkg/help>
+#include <bpkg/cfg-create>
#include <bpkg/rep-create>
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<cfg_create_options> (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 <brep/types-parsers>"
+
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 <bpkg/types>
+#include <bpkg/cfg-create-options>
+
+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 <bpkg/common-options.cli>;
+
+/*
+"\section=1"
+"\name=bpkg-cfg-create"
+
+"\h{SYNOPSIS}
+
+bpkg cfg-create [<options>] [(<module>|<conf-var>)...]"
+
+"\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 (".")
+ {
+ "<dir>",
+ "Create configuration in <dir> 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 <bpkg/cfg-create>
+
+#include <utility> // move()
+#include <cassert>
+#include <fstream>
+#include <iostream>
+
+#include <bpkg/types>
+#include <bpkg/utility>
+#include <bpkg/diagnostics>
+
+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 <cstdint>;
+include <bpkg/types>;
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 <bpkg/types>
+
+namespace bpkg
+{
+ namespace cli
+ {
+ class scanner;
+
+ template <typename T>
+ struct parser;
+
+ template <>
+ struct parser<dir_path>
+ {
+ 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 <bpkg/types-parsers>
+
+#include <bpkg/common-options> // bpkg::cli namespace
+
+namespace bpkg
+{
+ namespace cli
+ {
+ void parser<dir_path>::
+ 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 <system_error>
+#include <butl/process>
#include <butl/filesystem>
#include <bpkg/types>
@@ -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 ();
+ }
+ }
}