aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-01-16 09:34:47 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-01-16 09:34:47 +0200
commite2d59d4a05b7624570c6398b3a6d095138d0d241 (patch)
tree4e5d1fbe19c09bbf0ac1dd933ad3543cfed40fba
parent2862f91f6016bd07fd9adc0a0f518258a32aa274 (diff)
Implement temp directory facility
-rw-r--r--bpkg/bpkg.cxx61
-rw-r--r--bpkg/cfg-create.cxx4
-rw-r--r--bpkg/utility.cxx53
-rw-r--r--bpkg/utility.hxx33
4 files changed, 119 insertions, 32 deletions
diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx
index 0bba61a..4385567 100644
--- a/bpkg/bpkg.cxx
+++ b/bpkg/bpkg.cxx
@@ -46,6 +46,17 @@
using namespace std;
using namespace bpkg;
+// Get -d|--directory value if the option class O has it and empty path
+// otherwise. Note that for some commands (like rep-info) that allow
+// specifying empty path, the returned value is a string, not a dir_path.
+//
+template <typename O>
+static inline auto
+cfg_dir (const O* o) -> decltype(o->directory ()) {return o->directory ();}
+
+static inline auto
+cfg_dir (...) -> const dir_path& {return empty_dir_path;}
+
// Initialize the command option class O with the common options and then
// parse the rest of the command line placing non-option arguments to args.
// Once this is done, use the "final" values of the common options to do
@@ -53,7 +64,7 @@ using namespace bpkg;
//
template <typename O>
static O
-parse (const common_options& co, cli::scanner& scan, strings& args)
+init (const common_options& co, cli::scanner& scan, strings& args, bool tmp)
{
O o;
static_cast<common_options&> (o) = co;
@@ -96,6 +107,11 @@ parse (const common_options& co, cli::scanner& scan, strings& args)
? o.verbose ()
: o.V () ? 3 : o.v () ? 2 : o.quiet () ? 0 : 1;
+ // Temporary directory.
+ //
+ if (tmp)
+ init_tmp (dir_path (cfg_dir (&o)));
+
return o;
}
@@ -165,7 +181,7 @@ try
const common_options& co (o);
if (o.help ())
- return help (parse<help_options> (co, scan, argsv), "", nullptr);
+ return help (init<help_options> (co, scan, argsv, false), "", nullptr);
// The next argument should be a command.
//
@@ -191,7 +207,7 @@ try
if (h)
{
- ho = parse<help_options> (co, scan, argsv);
+ ho = init<help_options> (co, scan, argsv, false);
if (args.more ())
{
@@ -215,7 +231,8 @@ try
// Handle commands.
//
int r (1);
- for (;;)
+ for (;;) // Breakout loop.
+ try
{
// help
//
@@ -233,31 +250,31 @@ try
// if (h)
// r = help (ho, "pkg-verify", print_bpkg_pkg_verify_usage);
// else
- // r = pkg_verify (parse<pkg_verify_options> (co, scan, argsv), args);
+ // r = pkg_verify (init<pkg_verify_options> (co, scan, argsv, TMP), args);
//
// break;
// }
//
-#define COMMAND_IMPL(NP, SP, CMD) \
- if (cmd.NP##CMD ()) \
- { \
- if (h) \
- r = help (ho, SP#CMD, print_bpkg_##NP##CMD##_usage); \
- else \
- r = NP##CMD (parse<NP##CMD##_options> (co, scan, argsv), args); \
- \
- break; \
+#define COMMAND_IMPL(NP, SP, CMD, TMP) \
+ if (cmd.NP##CMD ()) \
+ { \
+ if (h) \
+ r = help (ho, SP#CMD, print_bpkg_##NP##CMD##_usage); \
+ else \
+ r = NP##CMD (init<NP##CMD##_options> (co, scan, argsv, TMP), args); \
+ \
+ break; \
}
// cfg-* commands
//
-#define CFG_COMMAND(CMD) COMMAND_IMPL(cfg_, "cfg-", CMD)
+#define CFG_COMMAND(CMD, TMP) COMMAND_IMPL(cfg_, "cfg-", CMD, TMP)
- CFG_COMMAND (create);
+ CFG_COMMAND (create, false); // Temp dir initialized manually.
// pkg-* commands
//
-#define PKG_COMMAND(CMD) COMMAND_IMPL(pkg_, "pkg-", CMD)
+#define PKG_COMMAND(CMD) COMMAND_IMPL(pkg_, "pkg-", CMD, true)
PKG_COMMAND (build);
PKG_COMMAND (clean);
@@ -276,7 +293,7 @@ try
// rep-* commands
//
-#define REP_COMMAND(CMD) COMMAND_IMPL(rep_, "rep-", CMD)
+#define REP_COMMAND(CMD) COMMAND_IMPL(rep_, "rep-", CMD, true)
REP_COMMAND (add);
REP_COMMAND (create);
@@ -286,6 +303,14 @@ try
assert (false);
fail << "unhandled command";
}
+ catch (const failed&)
+ {
+ r = 1;
+ break;
+ }
+
+ if (!tmp_dir.empty ())
+ clean_tmp (true /* ignore_error */);
if (r != 0)
return r;
diff --git a/bpkg/cfg-create.cxx b/bpkg/cfg-create.cxx
index 7592801..acd6bb9 100644
--- a/bpkg/cfg-create.cxx
+++ b/bpkg/cfg-create.cxx
@@ -87,6 +87,10 @@ namespace bpkg
//
mk (c / bpkg_dir);
+ // Initialize tmp directory.
+ //
+ init_tmp (c);
+
// Create the database.
//
database db (open (c, trace, true));
diff --git a/bpkg/utility.cxx b/bpkg/utility.cxx
index 8ecda93..d87c151 100644
--- a/bpkg/utility.cxx
+++ b/bpkg/utility.cxx
@@ -17,9 +17,42 @@ using namespace butl;
namespace bpkg
{
+ const string empty_string;
+ const path empty_path;
+ const dir_path empty_dir_path;
+
const dir_path bpkg_dir (".bpkg");
const dir_path certs_dir (dir_path (bpkg_dir) /= "certs");
+ dir_path tmp_dir;
+
+ void
+ init_tmp (const dir_path& cfg)
+ {
+ // Whether the configuration is required or optional depends on the
+ // command so if the configuration directory does not exist, we simply
+ // create tmp in a system one and let the command complain if necessary.
+ //
+ dir_path d (cfg.empty () || !exists (cfg, true /* ignore_error */)
+ ? dir_path::temp_path ("bpkg")
+ : cfg / bpkg_dir / dir_path ("tmp"));
+
+ if (exists (d))
+ rm_r (d, true /* dir_itself */, 1); // Verbose to avoid surprises.
+
+ mk (d); // We shouldn't need mk_p().
+
+ tmp_dir = move (d);
+ }
+
+ void
+ clean_tmp (bool ignore_error)
+ {
+ assert (!tmp_dir.empty ());
+ rm_r (tmp_dir, true /* dir_itself */, 3, ignore_error);
+ tmp_dir.clear ();
+ }
+
bool
yn_prompt (const char* prompt, char def)
{
@@ -60,11 +93,11 @@ namespace bpkg
}
bool
- exists (const path& f)
+ exists (const path& f, bool ignore_error)
{
try
{
- return file_exists (f);
+ return file_exists (f, true /* follow_symlinks */, ignore_error);
}
catch (const system_error& e)
{
@@ -73,11 +106,11 @@ namespace bpkg
}
bool
- exists (const dir_path& d)
+ exists (const dir_path& d, bool ignore_error)
{
try
{
- return dir_exists (d);
+ return dir_exists (d, ignore_error);
}
catch (const system_error& e)
{
@@ -131,9 +164,9 @@ namespace bpkg
}
void
- rm (const path& f)
+ rm (const path& f, uint16_t v)
{
- if (verb >= 3)
+ if (verb >= v)
text << "rm " << f;
try
@@ -148,14 +181,14 @@ namespace bpkg
}
void
- rm_r (const dir_path& d, bool dir)
+ rm_r (const dir_path& d, bool dir, uint16_t v, bool ignore_error)
{
- if (verb >= 3)
- text << "rmdir -r " << d << (dir ? "" : "*");
+ if (verb >= v)
+ text << (dir ? "rmdir -r " : "rm -r ") << (dir ? d : d / dir_path ("*"));
try
{
- rmdir_r (d, dir);
+ rmdir_r (d, dir, ignore_error);
}
catch (const system_error& e)
{
diff --git a/bpkg/utility.hxx b/bpkg/utility.hxx
index 8a1107c..e56e093 100644
--- a/bpkg/utility.hxx
+++ b/bpkg/utility.hxx
@@ -39,11 +39,33 @@ namespace bpkg
using butl::exception_guard;
using butl::make_exception_guard;
+ // Empty string and path.
+ //
+ extern const string empty_string;
+ extern const path empty_path;
+ extern const dir_path empty_dir_path;
+
// Widely-used paths.
//
extern const dir_path bpkg_dir; // .bpkg/
extern const dir_path certs_dir; // .bpkg/certs/
+ // Temporary directory.
+ //
+ // This is normally .bpkg/tmp/ but can also be some system-wide directory
+ // (e.g., /tmp/bpkg-XXX/) if there is no bpkg configuration. This directory
+ // is automatically created and cleaned up for most commands in main() so
+ // you don't need to call init_tmp() explicitly except for certain special
+ // commands (like cfg-create).
+ //
+ extern dir_path tmp_dir;
+
+ void
+ init_tmp (const dir_path& cfg);
+
+ void
+ clean_tmp (bool ignore_errors);
+
// Y/N prompt. The def argument, if specified, should be either 'y'
// or 'n'. It is used as the default answer, in case the user just
// hits enter. Issue diagnostics and throw failed if no answer could
@@ -55,10 +77,10 @@ namespace bpkg
// Filesystem.
//
bool
- exists (const path&);
+ exists (const path&, bool ignore_error = false);
bool
- exists (const dir_path&);
+ exists (const dir_path&, bool ignore_error = false);
bool
empty (const dir_path&);
@@ -70,10 +92,13 @@ namespace bpkg
mk_p (const dir_path&);
void
- rm (const path&);
+ rm (const path&, uint16_t verbosity = 3);
void
- rm_r (const dir_path&, bool dir = true);
+ rm_r (const dir_path&,
+ bool dir_itself = true,
+ uint16_t verbosity = 3,
+ bool ignore_error = false);
using auto_rm = butl::auto_rmfile;
using auto_rm_r = butl::auto_rmdir;