From f283fbea934a1a2bad3fa1df25a82717e6b11aac Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 18 Jul 2018 20:43:41 +0300 Subject: Implement temp directory facility --- bdep/bdep.cxx | 73 ++++++++++++++++++++++++++++++++++++++------------------ bdep/new.cxx | 4 ++++ bdep/publish.cxx | 2 +- bdep/sync.cxx | 8 +++++++ bdep/utility.cxx | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bdep/utility.hxx | 30 +++++++++++++++++++++++ 6 files changed, 166 insertions(+), 24 deletions(-) diff --git a/bdep/bdep.cxx b/bdep/bdep.cxx index 339be05..f77870f 100644 --- a/bdep/bdep.cxx +++ b/bdep/bdep.cxx @@ -12,6 +12,7 @@ #include #include +#include // find_project() #include #include #include @@ -68,9 +69,25 @@ cfg_name (...) return false; } +// Find the project directory if it is possible for the option class O and +// return empty path otherwise. +// +template +static inline auto +prj_dir (const O* o) -> decltype(find_project (*o)) +{ + return find_project (*o); +} + +static inline auto +prj_dir (...) -> const dir_path& {return empty_dir_path;} + template static O -init (const common_options& co, cli::group_scanner& scan, strings& args) +init (const common_options& co, + cli::group_scanner& scan, + strings& args, + bool tmp) { O o; static_cast (o) = co; @@ -124,6 +141,11 @@ init (const common_options& co, cli::group_scanner& scan, strings& args) ? o.verbose () : o.V () ? 3 : o.v () ? 2 : o.quiet () ? 0 : 1; + // Temporary directory. + // + if (tmp) + init_tmp (dir_path (prj_dir (&o))); + return o; } @@ -196,7 +218,7 @@ try const common_options& co (o); if (o.help ()) - return help (init (co, scan, argsv), "", nullptr); + return help (init (co, scan, argsv, false), "", nullptr); // The next argument should be a command. // @@ -211,7 +233,7 @@ try if (h) { - ho = init (co, scan, argsv); + ho = init (co, scan, argsv, false); if (args.more ()) { @@ -255,28 +277,31 @@ try // break; // } // -#define COMMAND_IMPL(ON, FN, SN) \ - if (cmd.ON ()) \ - { \ - if (h) \ - r = help (ho, SN, print_bdep_##FN##_usage); \ - else \ - r = cmd_##FN (init (co, scan, argsv), args); \ - \ - break; \ +#define COMMAND_IMPL(ON, FN, SN, TMP) \ + if (cmd.ON ()) \ + { \ + if (h) \ + r = help (ho, SN, print_bdep_##FN##_usage); \ + else \ + r = cmd_##FN (init (co, scan, argsv, TMP), args); \ + \ + break; \ } - COMMAND_IMPL (new_, new, "new"); - COMMAND_IMPL (init, init, "init"); - COMMAND_IMPL (sync, sync, "sync"); - COMMAND_IMPL (fetch, fetch, "fetch"); - COMMAND_IMPL (status, status, "status"); - COMMAND_IMPL (publish, publish, "publish"); - COMMAND_IMPL (deinit, deinit, "deinit"); - COMMAND_IMPL (config, config, "config"); - COMMAND_IMPL (test, test, "test"); - COMMAND_IMPL (update, update, "update"); - COMMAND_IMPL (clean, clean, "clean"); + // Temp dir is initialized manually for these commands. + // + COMMAND_IMPL (new_, new, "new", false); + COMMAND_IMPL (sync, sync, "sync", false); + + COMMAND_IMPL (init, init, "init", true); + COMMAND_IMPL (fetch, fetch, "fetch", true); + COMMAND_IMPL (status, status, "status", true); + COMMAND_IMPL (publish, publish, "publish", true); + COMMAND_IMPL (deinit, deinit, "deinit", true); + COMMAND_IMPL (config, config, "config", true); + COMMAND_IMPL (test, test, "test", true); + COMMAND_IMPL (update, update, "update", true); + COMMAND_IMPL (clean, clean, "clean", true); assert (false); fail << "unhandled command"; @@ -287,6 +312,8 @@ try break; } + clean_tmp (true /* ignore_error */); + if (r != 0) return r; diff --git a/bdep/new.cxx b/bdep/new.cxx index dc86b72..2287431 100644 --- a/bdep/new.cxx +++ b/bdep/new.cxx @@ -1105,6 +1105,10 @@ namespace bdep // mk (prj / bdep_dir); + // Initialize tmp directory. + // + init_tmp (prj); + // Everything else requires a database. // database db (open (prj, trace, true /* create */)); diff --git a/bdep/publish.cxx b/bdep/publish.cxx index 0fbe02d..2a74262 100644 --- a/bdep/publish.cxx +++ b/bdep/publish.cxx @@ -333,7 +333,7 @@ namespace bdep // Prepare package archives and calculate their checksums. Also verify // each archive with bpkg-pkg-verify for good measure. // - auto_rmdir dr_rm (dir_path ("/tmp/publish")); //@@ TODO tmp facility like in bpkg. + auto_rmdir dr_rm (tmp_dir ("publish")); const dir_path& dr (dr_rm.path); // dist.root mk (dr); diff --git a/bdep/sync.cxx b/bdep/sync.cxx index 1620555..ff37925 100644 --- a/bdep/sync.cxx +++ b/bdep/sync.cxx @@ -656,6 +656,10 @@ namespace bdep !dep_pkgs.empty () /* ignore_packages */, false /* load_packages */)); + // Initialize tmp directory. + // + init_tmp (pp.project); + // Load project configurations. // { @@ -713,6 +717,10 @@ namespace bdep if (cfgs.empty ()) return 0; // All configuration are already (being) synchronized. + + // Initialize tmp directory. + // + init_tmp (empty_dir_path); } // Synchronize each configuration. diff --git a/bdep/utility.cxx b/bdep/utility.cxx index 2600de9..45f924a 100644 --- a/bdep/utility.cxx +++ b/bdep/utility.cxx @@ -29,6 +29,57 @@ namespace bdep const char* argv0; dir_path exec_dir; + dir_path temp_dir; + + auto_rmfile + tmp_file (const string& p) + { + assert (!temp_dir.empty ()); + return auto_rmfile (temp_dir / path::traits::temp_name (p)); + } + + auto_rmdir + tmp_dir (const string& p) + { + assert (!temp_dir.empty ()); + return auto_rmdir (temp_dir / dir_path (path::traits::temp_name (p))); + } + + void + init_tmp (const dir_path& prj) + { + // Whether the project is required or optional depends on the command so + // if the project directory does not exist or it is not a bdep project + // directory, we simply create tmp in a system one and let the command + // complain if necessary. + // + dir_path d (prj.empty () || + !exists (prj / bdep_dir, true /* ignore_error */) + ? dir_path::temp_path ("bdep") + : prj / bdep_dir / dir_path ("tmp")); + + if (exists (d)) + rm_r (d, true /* dir_itself */, 2); + + mk (d); // We shouldn't need mk_p(). + + temp_dir = move (d); + } + + void + clean_tmp (bool ignore_error) + { + if (!temp_dir.empty () && exists (temp_dir)) + { + rm_r (temp_dir, + true /* dir_itself */, + 3, + ignore_error ? rm_error_mode::ignore : rm_error_mode::fail); + + temp_dir.clear (); + } + } + bool exists (const path& f, bool ignore_error) { @@ -101,6 +152,28 @@ namespace bdep } } + void + rm_r (const dir_path& d, bool dir, uint16_t v, rm_error_mode m) + { + if (verb >= v) + text << (dir ? "rmdir -r " : "rm -r ") << (dir ? d : d / dir_path ("*")); + + try + { + rmdir_r (d, dir, m == rm_error_mode::ignore); + } + catch (const system_error& e) + { + bool w (m == rm_error_mode::warn); + + (w ? warn : error) << "unable to remove " << (dir ? "" : "contents of ") + << "directory " << d << ": " << e; + + if (!w) + throw failed (); + } + } + const char* name_bpkg (const common_options& co) { diff --git a/bdep/utility.hxx b/bdep/utility.hxx index 7bc1522..a8a755b 100644 --- a/bdep/utility.hxx +++ b/bdep/utility.hxx @@ -84,6 +84,28 @@ namespace bdep extern const path repositories_file; // repositories.manifest extern const path configurations_file; // configurations.manifest + // Temporary directory facility. + // + // This is normally .bdep/tmp/ but can also be some system-wide directory + // (e.g., /tmp/bdep-XXX/) if there is no bdep project. 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 new). + // + extern dir_path temp_dir; + + auto_rmfile + tmp_file (const string& prefix); + + auto_rmdir + tmp_dir (const string& prefix); + + void + init_tmp (const dir_path& prj); + + void + clean_tmp (bool ignore_errors); + // Process path (argv[0]). // extern const char* argv0; @@ -113,6 +135,14 @@ namespace bdep void rm (const path&, uint16_t verbosity = 3); + enum class rm_error_mode {ignore, warn, fail}; + + void + rm_r (const dir_path&, + bool dir_itself = true, + uint16_t verbosity = 3, + rm_error_mode = rm_error_mode::fail); + // Run a process. // template -- cgit v1.1