From e2d59d4a05b7624570c6398b3a6d095138d0d241 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 16 Jan 2018 09:34:47 +0200 Subject: Implement temp directory facility --- bpkg/bpkg.cxx | 61 +++++++++++++++++++++++++++++++++++++---------------- bpkg/cfg-create.cxx | 4 ++++ bpkg/utility.cxx | 53 +++++++++++++++++++++++++++++++++++++--------- bpkg/utility.hxx | 33 +++++++++++++++++++++++++---- 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 +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 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 (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 (co, scan, argsv), "", nullptr); + return help (init (co, scan, argsv, false), "", nullptr); // The next argument should be a command. // @@ -191,7 +207,7 @@ try if (h) { - ho = parse (co, scan, argsv); + ho = init (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 (co, scan, argsv), args); + // r = pkg_verify (init (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 (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 (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; -- cgit v1.1