From 542ad9696f50e33fa20e735c14c052720c55bc3a Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 23 Aug 2019 14:35:53 +0200 Subject: dry_run --- build2/b.cxx | 2 +- build2/cc/compile-rule.cxx | 22 +++++++------ build2/cc/init.cxx | 8 +++-- build2/cc/link-rule.cxx | 20 ++++++------ build2/cc/pkgconfig.cxx | 2 +- build2/cc/windows-manifest.cxx | 2 +- build2/cc/windows-rpath.cxx | 4 +-- build2/cli/rule.cxx | 2 +- libbuild2/algorithm.cxx | 49 +++++++++++++++------------- libbuild2/algorithm.hxx | 9 ++++-- libbuild2/config/operation.cxx | 18 +++++------ libbuild2/context.cxx | 5 ++- libbuild2/context.hxx | 69 +++++++++++++++++++++------------------- libbuild2/dist/operation.cxx | 22 +++++++------ libbuild2/filesystem.cxx | 28 +++++++++------- libbuild2/filesystem.hxx | 55 ++++++++++++++++---------------- libbuild2/filesystem.ixx | 40 +++++++++++++++++++++++ libbuild2/filesystem.txx | 9 +++--- libbuild2/install/rule.cxx | 24 ++++++++------ libbuild2/operation.cxx | 4 +-- libbuild2/test/rule.cxx | 36 ++++++++++++--------- libbuild2/test/script/runner.cxx | 27 +++++++++------- libbuild2/utility.cxx | 3 -- libbuild2/utility.hxx | 3 -- libbuild2/version/init.cxx | 3 +- libbuild2/version/rule.cxx | 3 +- libbuild2/version/utility.cxx | 9 ++++-- libbuild2/version/utility.hxx | 6 +++- 28 files changed, 285 insertions(+), 199 deletions(-) create mode 100644 libbuild2/filesystem.ixx diff --git a/build2/b.cxx b/build2/b.cxx index 0f1009a..3b19d07 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -478,7 +478,6 @@ main (int argc, char* argv[]) // init (&::terminate, argv[0], - ops.dry_run (), (ops.mtime_check () ? optional (true) : ops.no_mtime_check () ? optional (false) : nullopt), (ops.config_sub_specified () @@ -621,6 +620,7 @@ main (int argc, char* argv[]) ctx = nullptr; // Free first. ctx.reset (new context (sched, cmd_vars, + ops.dry_run (), !ops.serial_stop () /* keep_going */)); }; diff --git a/build2/cc/compile-rule.cxx b/build2/cc/compile-rule.cxx index 6902bb6..fa43533 100644 --- a/build2/cc/compile-rule.cxx +++ b/build2/cc/compile-rule.cxx @@ -583,6 +583,8 @@ namespace build2 match_data& md (t.data ()); + context& ctx (t.ctx); + // Note: until refined below, non-BMI-generating translation unit is // assumed non-modular. // @@ -698,7 +700,7 @@ namespace build2 // Start asynchronous matching of prerequisites. Wait with unlocked // phase to allow phase switching. // - wait_guard wg (t.ctx, t.ctx.count_busy (), t[a].task_count, true); + wait_guard wg (ctx, ctx.count_busy (), t[a].task_count, true); size_t start (pts.size ()); // Index of the first to be added. for (prerequisite_member p: group_prerequisite_members (a, t)) @@ -760,7 +762,7 @@ namespace build2 continue; } - match_async (a, *pt, t.ctx.count_busy (), t[a].task_count); + match_async (a, *pt, ctx.count_busy (), t[a].task_count); pts.push_back (prerequisite_target (pt, pi)); } @@ -1148,7 +1150,7 @@ namespace build2 // to keep re-validating the file on every subsequent dry-run as well // on the real run). // - if (u && dd.reading () && !dry_run) + if (u && dd.reading () && !ctx.dry_run) dd.touch = true; dd.close (); @@ -5558,6 +5560,8 @@ namespace build2 match_data md (move (t.data ())); unit_type ut (md.type); + context& ctx (t.ctx); + // While all our prerequisites are already up-to-date, we still have to // execute them to keep the dependency counts straight. Actually, no, we // may also have to update the modules. @@ -5583,9 +5587,9 @@ namespace build2 { if (md.touch) { - touch (tp, false, 2); + touch (ctx, tp, false, 2); t.mtime (system_clock::now ()); - t.ctx.skip_count.fetch_add (1, memory_order_relaxed); + ctx.skip_count.fetch_add (1, memory_order_relaxed); } // Note: else mtime should be cached. @@ -5600,7 +5604,7 @@ namespace build2 ? system_clock::now () : timestamp_unknown); - touch (md.dd, false, verb_never); + touch (ctx, md.dd, false, verb_never); const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); @@ -5949,7 +5953,7 @@ namespace build2 // translation unit (i.e., one of the imported module's has BMIs // changed). // - if (!dry_run) + if (!ctx.dry_run) { try { @@ -6028,7 +6032,7 @@ namespace build2 if (verb >= 2) print_process (args); - if (!dry_run) + if (!ctx.dry_run) { // Remove the target file if this fails. If we don't do that, we // will end up with a broken build that is up-to-date. @@ -6061,7 +6065,7 @@ namespace build2 timestamp now (system_clock::now ()); - if (!dry_run) + if (!ctx.dry_run) depdb::check_mtime (start, md.dd, tp, now); // Should we go to the filesystem and get the new mtime? We know the diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx index 3ff59a1..c83d5ed 100644 --- a/build2/cc/init.cxx +++ b/build2/cc/init.cxx @@ -26,27 +26,29 @@ namespace build2 static target_state clean_module_sidebuilds (action, const scope& rs, const dir&) { + context& ctx (rs.ctx); + const dir_path& out_root (rs.out_path ()); dir_path d (out_root / rs.root_extra->build_dir / modules_sidebuild_dir); if (exists (d)) { - if (build2::rmdir_r (d)) + if (rmdir_r (ctx, d)) { // Clean up cc/ if it became empty. // d = out_root / rs.root_extra->build_dir / module_dir; if (empty (d)) { - rmdir (d); + rmdir (ctx, d); // And build/ if it also became empty (e.g., in case of a build // with a transient configuration). // d = out_root / rs.root_extra->build_dir; if (empty (d)) - rmdir (d); + rmdir (ctx, d); } return target_state::changed; diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx index 9980fa3..bfc31d7 100644 --- a/build2/cc/link-rule.cxx +++ b/build2/cc/link-rule.cxx @@ -1749,6 +1749,8 @@ namespace build2 const file& t (xt.as ()); const path& tp (t.path ()); + context& ctx (t.ctx); + const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); @@ -1868,7 +1870,7 @@ namespace build2 if (verb >= 3) print_process (args); - if (!dry_run) + if (!ctx.dry_run) { auto_rmfile rm (of); @@ -1968,7 +1970,7 @@ namespace build2 const string& cs ( cast ( rs[tsys == "win32-msvc" - ? t.ctx.var_pool["bin.ld.checksum"] + ? ctx.var_pool["bin.ld.checksum"] : x_checksum])); if (dd.expect (cs) != nullptr) @@ -2728,7 +2730,7 @@ namespace build2 // auto_rmfile rm; - if (!dry_run) + if (!ctx.dry_run) { rm = auto_rmfile (relt); @@ -2825,7 +2827,7 @@ namespace build2 if (verb >= 2) print_process (args); - if (!dry_run) + if (!ctx.dry_run) run (rl, args); } @@ -2845,12 +2847,12 @@ namespace build2 // For shared libraries we may need to create a bunch of symlinks (or // fallback to hardlinks/copies on Windows). // - auto ln = [] (const path& f, const path& l) + auto ln = [&ctx] (const path& f, const path& l) { if (verb >= 3) text << "ln -sf " << f << ' ' << l; - if (dry_run) + if (ctx.dry_run) return; try @@ -2910,12 +2912,12 @@ namespace build2 // if (tsys == "darwin" && cast (rs["bin.ar.id"]) == "generic") { - if (!dry_run) - touch (tp, false /* create */, verb_never); + if (!ctx.dry_run) + touch (ctx, tp, false /* create */, verb_never); } } - if (!dry_run) + if (!ctx.dry_run) { rm.cancel (); dd.check_mtime (tp); diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx index 038ecc6..3b4c711 100644 --- a/build2/cc/pkgconfig.cxx +++ b/build2/cc/pkgconfig.cxx @@ -1248,7 +1248,7 @@ namespace build2 if (verb >= 2) text << "cat >" << p; - if (dry_run) + if (ctx.dry_run) return; auto_rmfile arm (p); diff --git a/build2/cc/windows-manifest.cxx b/build2/cc/windows-manifest.cxx index da12f0f..733cae5 100644 --- a/build2/cc/windows-manifest.cxx +++ b/build2/cc/windows-manifest.cxx @@ -119,7 +119,7 @@ namespace build2 if (verb >= 3) text << "cat >" << mf; - if (!dry_run) + if (!t.ctx.dry_run) { auto_rmfile rm (mf); diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx index d81e3b0..4478f7d 100644 --- a/build2/cc/windows-rpath.cxx +++ b/build2/cc/windows-rpath.cxx @@ -281,7 +281,7 @@ namespace build2 // simpler. // { - rmdir_status s (build2::rmdir_r (ad, empty, 3)); + rmdir_status s (rmdir_r (t.ctx, ad, empty, 3)); if (empty) return; @@ -362,7 +362,7 @@ namespace build2 if (verb >= 3) text << "cat >" << am; - if (dry_run) + if (t.ctx.dry_run) return; auto_rmfile rm (am); diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx index f6c0763..4ccafc9 100644 --- a/build2/cli/rule.cxx +++ b/build2/cli/rule.cxx @@ -322,7 +322,7 @@ namespace build2 else if (verb) text << "cli " << s; - if (!dry_run) + if (!t.ctx.dry_run) { run (cli, args); dd.check_mtime (tp); diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx index 8bc6ee1..2d2c940 100644 --- a/libbuild2/algorithm.cxx +++ b/libbuild2/algorithm.cxx @@ -1095,11 +1095,12 @@ namespace build2 if (!exists (d)) mkdir_p (d, 2 /* verbosity */); - update_backlink (p, l, m); + update_backlink (f.ctx, p, l, m); } void - update_backlink (const path& p, const path& l, bool changed, backlink_mode m) + update_backlink (context& ctx, + const path& p, const path& l, bool changed, backlink_mode m) { // As above but with a slightly different diagnostics. @@ -1133,7 +1134,7 @@ namespace build2 if (!exists (d)) mkdir_p (d, 2 /* verbosity */); - update_backlink (p, l, m); + update_backlink (ctx, p, l, m); } static inline void @@ -1172,7 +1173,8 @@ namespace build2 } void - update_backlink (const path& p, const path& l, backlink_mode om) + update_backlink (context& ctx, + const path& p, const path& l, backlink_mode om) { using mode = backlink_mode; @@ -1203,7 +1205,7 @@ namespace build2 { // Normally will be there. // - if (!dry_run) + if (!ctx.dry_run) try_rmbacklink (l, m); // Skip (ad hoc) targets that don't exist. @@ -1247,7 +1249,7 @@ namespace build2 path f (fr / de.path ()); path t (to / de.path ()); - update_backlink (f, t, mode::link); + update_backlink (ctx, f, t, mode::link); } } else @@ -1288,7 +1290,8 @@ namespace build2 } void - clean_backlink (const path& l, uint16_t v /*verbosity*/, backlink_mode m) + clean_backlink (context& ctx, + const path& l, uint16_t v /*verbosity*/, backlink_mode m) { // Like try_rmbacklink() but with diagnostics and error handling. @@ -1300,9 +1303,9 @@ namespace build2 { case mode::link: case mode::symbolic: - case mode::hard: rmsymlink (l, true /* directory */, v); break; - case mode::copy: rmdir_r (path_cast (l), true, v); break; - case mode::overwrite: break; + case mode::hard: rmsymlink (ctx, l, true /* directory */, v); break; + case mode::copy: rmdir_r (ctx, path_cast (l), true, v); break; + case mode::overwrite: break; } } else @@ -1314,8 +1317,8 @@ namespace build2 case mode::link: case mode::symbolic: case mode::hard: - case mode::copy: rmfile (l, v); break; - case mode::overwrite: break; + case mode::copy: rmfile (ctx, l, v); break; + case mode::overwrite: break; } } } @@ -1497,7 +1500,7 @@ namespace build2 ts == target_state::changed, bl.mode); else - update_backlink (bl.target, bl.path, bl.mode); + update_backlink (t.ctx, bl.target, bl.path, bl.mode); } // Cancel removal. @@ -1517,7 +1520,7 @@ namespace build2 // backlink& bl (*i); bl.cancel (); - clean_backlink (bl.path, i == b ? 2 : 3 /* verbosity */, bl.mode); + clean_backlink (t.ctx, bl.path, i == b ? 2 : 3 /* verbosity */, bl.mode); } } @@ -2038,9 +2041,9 @@ namespace build2 context& ctx (ft.ctx); - auto clean_extra = [&er, &ed, &ep] (const file& f, - const path* fp, - const clean_extras& es) + auto clean_extra = [&er, &ed, &ep, &ctx] (const file& f, + const path* fp, + const clean_extras& es) { for (const char* e: es) { @@ -2080,7 +2083,7 @@ namespace build2 { dir_path dp (path_cast (p)); - switch (build2::rmdir_r (dp, true, 3)) + switch (rmdir_r (ctx, dp, true, 3)) { case rmdir_status::success: { @@ -2099,7 +2102,7 @@ namespace build2 } else { - if (rmfile (p, 3)) + if (rmfile (ctx, p, 3)) r = target_state::changed; } @@ -2165,7 +2168,7 @@ namespace build2 } else { - target_state r (rmfile (*mp, 3) + target_state r (rmfile (ctx, *mp, 3) ? target_state::changed : target_state::unchanged); @@ -2261,6 +2264,8 @@ namespace build2 target_state perform_clean_group_depdb (action a, const target& g) { + context& ctx (g.ctx); + // The same twisted target state merging logic as in perform_clean_extra(). // target_state er (target_state::unchanged); @@ -2271,7 +2276,7 @@ namespace build2 { ep = gv.members[0]->as ().path () + ".d"; - if (rmfile (ep, 3)) + if (rmfile (ctx, ep, 3)) er = target_state::changed; } @@ -2279,7 +2284,7 @@ namespace build2 if (tr != target_state::changed && er == target_state::changed) { - if (verb > (g.ctx.current_diag_noise ? 0 : 1) && verb < 3) + if (verb > (ctx.current_diag_noise ? 0 : 1) && verb < 3) text << "rm " << ep; } diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx index d772469..cda464b 100644 --- a/libbuild2/algorithm.hxx +++ b/libbuild2/algorithm.hxx @@ -761,18 +761,21 @@ namespace build2 backlink_mode = backlink_mode::link); LIBBUILD2_SYMEXPORT void - update_backlink (const path& target, + update_backlink (context&, + const path& target, const path& link, bool changed, backlink_mode = backlink_mode::link); LIBBUILD2_SYMEXPORT void - update_backlink (const path& target, + update_backlink (context&, + const path& target, const path& link, backlink_mode = backlink_mode::link); LIBBUILD2_SYMEXPORT void - clean_backlink (const path& link, + clean_backlink (context&, + const path& link, uint16_t verbosity, backlink_mode = backlink_mode::link); } diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index c5ff580..264eb93 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -635,7 +635,7 @@ namespace build2 !d.empty (); d = d.directory ()) { - rmdir_status s (rmdir (out_root / d, 2)); + rmdir_status s (rmdir (ctx, out_root / d, 2)); if (s == rmdir_status::not_empty) break; // No use trying do remove parent ones. @@ -653,21 +653,21 @@ namespace build2 { l5 ([&]{trace << "completely disfiguring " << out_root;}); - r = rmfile (config_file (rs)) || r; + r = rmfile (ctx, config_file (rs)) || r; if (out_root != src_root) { - r = rmfile (out_root / rs.root_extra->src_root_file, 2) || r; + r = rmfile (ctx, out_root / rs.root_extra->src_root_file, 2) || r; // Clean up the directories. // // Note: try to remove the root/ hooks directory if it is empty. // - r = rmdir (out_root / rs.root_extra->root_dir, 2) || r; - r = rmdir (out_root / rs.root_extra->bootstrap_dir, 2) || r; - r = rmdir (out_root / rs.root_extra->build_dir, 2) || r; + r = rmdir (ctx, out_root / rs.root_extra->root_dir, 2) || r; + r = rmdir (ctx, out_root / rs.root_extra->bootstrap_dir, 2) || r; + r = rmdir (ctx, out_root / rs.root_extra->build_dir, 2) || r; - switch (rmdir (out_root)) + switch (rmdir (ctx, out_root)) { case rmdir_status::not_empty: { @@ -731,8 +731,8 @@ namespace build2 // Remove the out-root.build file and try to remove the bootstrap/ // directory if it is empty. // - r = rmfile (src_root / rs.root_extra->out_root_file) || r; - r = rmdir (src_root / rs.root_extra->bootstrap_dir, 2) || r; + r = rmfile (ctx, src_root / rs.root_extra->out_root_file) || r; + r = rmdir (ctx, src_root / rs.root_extra->bootstrap_dir, 2) || r; return r; } diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx index 1b1b9f6..c454123 100644 --- a/libbuild2/context.cxx +++ b/libbuild2/context.cxx @@ -50,9 +50,10 @@ namespace build2 }; context:: - context (scheduler& s, const strings& cmd_vars, bool kg) + context (scheduler& s, const strings& cmd_vars, bool dr, bool kg) : data_ (new data (*this)), sched (s), + dry_run_option (dr), keep_going (kg), phase_mutex (phase), scopes (data_->scopes), @@ -852,8 +853,6 @@ namespace build2 //text << this_thread::get_id () << " phase restore " << n << " " << o; } - bool dry_run = false; - void (*config_save_variable) (scope&, const variable&, uint64_t); const string& (*config_preprocess_create) (context&, diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx index dbf2329..5060773 100644 --- a/libbuild2/context.hxx +++ b/libbuild2/context.hxx @@ -110,6 +110,41 @@ namespace build2 public: scheduler& sched; + // Dry run flag (see --dry-run|-n). + // + // This flag is set (based on dry_run_option) only for the final execute + // phase (as opposed to those that interrupt match) by the perform meta + // operation's execute() callback. + // + // Note that for this mode to function properly we have to use fake + // mtimes. Specifically, a rule that pretends to update a target must set + // its mtime to system_clock::now() and everyone else must use this cached + // value. In other words, there should be no mtime re-query from the + // filesystem. The same is required for "logical clean" (i.e., dry-run + // 'clean update' in order to see all the command lines). + // + // At first, it may seem like we should also "dry-run" changes to depdb. + // But that would be both problematic (some rules update it in apply() + // during the match phase) and wasteful (why discard information). Also, + // depdb may serve as an input to some commands (for example, to provide + // C++ module mapping) which means that without updating it the commands + // we print might not be runnable (think of the compilation database). + // + // One thing we need to be careful about if we are updating depdb is to + // not render the target up-to-date. But in this case the depdb file will + // be older than the target which in our model is treated as an + // interrupted update (see depdb for details). + // + // Note also that sometimes it makes sense to do a bit more than + // absolutely necessary or to discard information in order to keep the + // rule logic sane. And some rules may choose to ignore this flag + // altogether. In this case, however, the rule should be careful not to + // rely on functions (notably from filesystem) that respect this flag in + // order not to end up with a job half done. + // + bool dry_run = false; + bool dry_run_option; + // Keep going flag. // // Note that setting it to false is not of much help unless we are running @@ -336,6 +371,7 @@ namespace build2 explicit context (scheduler&, const strings& cmd_vars = {}, + bool dry_run = false, bool keep_going = true); // Set current meta-operation and operation. @@ -481,39 +517,6 @@ namespace build2 bool phase; }; - // Dry run flag (see --dry-run|-n). - // - // This flag is set only for the final execute phase (as opposed to those - // that interrupt match) by the perform meta operation's execute() callback. - // - // Note that for this mode to function properly we have to use fake mtimes. - // Specifically, a rule that pretends to update a target must set its mtime - // to system_clock::now() and everyone else must use this cached value. In - // other words, there should be no mtime re-query from the filesystem. The - // same is required for "logical clean" (i.e., dry-run 'clean update' in - // order to see all the command lines). - // - // At first, it may seem like we should also "dry-run" changes to depdb. But - // that would be both problematic (some rules update it in apply() during - // the match phase) and wasteful (why discard information). Also, depdb may - // serve as an input to some commands (for example, to provide C++ module - // mapping) which means that without updating it the commands we print might - // not be runnable (think of the compilation database). - // - // One thing we need to be careful about if we are updating depdb is to not - // render the target up-to-date. But in this case the depdb file will be - // older than the target which in our model is treated as an interrupted - // update (see depdb for details). - // - // Note also that sometimes it makes sense to do a bit more than absolutely - // necessary or to discard information in order to keep the rule logic sane. - // And some rules may choose to ignore this flag altogether. In this case, - // however, the rule should be careful not to rely on functions (notably - // from filesystem) that respect this flag in order not to end up with a - // job half done. - // - LIBBUILD2_SYMEXPORT extern bool dry_run; - // Config module entry points. // LIBBUILD2_SYMEXPORT extern void (*config_save_variable) ( diff --git a/libbuild2/dist/operation.cxx b/libbuild2/dist/operation.cxx index 2367329..ad2ee7f 100644 --- a/libbuild2/dist/operation.cxx +++ b/libbuild2/dist/operation.cxx @@ -44,7 +44,8 @@ namespace build2 // Return the archive file path. // static path - archive (const dir_path& root, + archive (context& ctx, + const dir_path& root, const string& pkg, const dir_path& dir, const string& ext); @@ -54,7 +55,8 @@ namespace build2 // Return the checksum file path. // static path - checksum (const path& arc, const dir_path& dir, const string& ext); + checksum (context&, + const path& arc, const dir_path& dir, const string& ext); static operation_id dist_operation_pre (const values&, operation_id o) @@ -336,7 +338,7 @@ namespace build2 // Clean up the target directory. // - if (build2::rmdir_r (td, true, 2) == rmdir_status::not_empty) + if (rmdir_r (ctx, td, true, 2) == rmdir_status::not_empty) fail << "unable to clean target directory " << td; auto_rmdir rm_td (td); // Clean it up if things go bad. @@ -469,14 +471,14 @@ namespace build2 for (const path& p: cast (as)) { auto ap (split (p, dist_root, "dist.archives")); - path a (archive (dist_root, dist_package, ap.first, ap.second)); + path a (archive (ctx, dist_root, dist_package, ap.first, ap.second)); if (cs) { for (const path& p: cast (cs)) { auto cp (split (p, ap.first, "dist.checksums")); - checksum (a, cp.first, cp.second); + checksum (ctx, a, cp.first, cp.second); } } } @@ -543,7 +545,8 @@ namespace build2 } static path - archive (const dir_path& root, + archive (context& ctx, + const dir_path& root, const string& pkg, const dir_path& dir, const string& e) @@ -554,7 +557,7 @@ namespace build2 // path ap (dir / an); if (exists (ap, false)) - rmfile (ap); + rmfile (ctx, ap); // Use zip for .zip archives. Also recognize and handle a few well-known // tar.xx cases (in case tar doesn't support -a or has other issues like @@ -715,7 +718,8 @@ namespace build2 } static path - checksum (const path& ap, const dir_path& dir, const string& e) + checksum (context& ctx, + const path& ap, const dir_path& dir, const string& e) { path an (ap.leaf ()); dir_path ad (ap.directory ()); @@ -726,7 +730,7 @@ namespace build2 // path cp (dir / cn); if (exists (cp, false)) - rmfile (cp); + rmfile (ctx, cp); auto_rmfile c_rm; // Note: must come first. auto_fd c_fd; diff --git a/libbuild2/filesystem.cxx b/libbuild2/filesystem.cxx index 83408fa..1cbaa58 100644 --- a/libbuild2/filesystem.cxx +++ b/libbuild2/filesystem.cxx @@ -13,12 +13,12 @@ using namespace butl; namespace build2 { void - touch (const path& p, bool create, uint16_t v) + touch (context& ctx, const path& p, bool create, uint16_t v) { if (verb >= v) text << "touch " << p; - if (dry_run) + if (ctx.dry_run) return; try @@ -104,7 +104,7 @@ namespace build2 } fs_status - rmsymlink (const path& p, bool d, uint16_t v) + rmsymlink (context& ctx, const path& p, bool d, uint16_t v) { auto print = [&p, v] () { @@ -116,7 +116,7 @@ namespace build2 try { - rs = dry_run + rs = ctx.dry_run ? (butl::entry_exists (p) ? rmfile_status::success : rmfile_status::not_exist) @@ -135,7 +135,7 @@ namespace build2 } fs_status - rmdir_r (const dir_path& d, bool dir, uint16_t v) + rmdir_r (context& ctx, const dir_path& d, bool dir, uint16_t v) { using namespace butl; @@ -148,7 +148,7 @@ namespace build2 if (verb >= v) text << "rmdir -r " << d; - if (!dry_run) + if (!ctx.dry_run) { try { @@ -216,7 +216,10 @@ namespace build2 } fs_status - mkdir_buildignore (const dir_path& d, const path& n, uint16_t verbosity) + mkdir_buildignore (context& ctx, + const dir_path& d, + const path& n, + uint16_t verbosity) { fs_status r (mkdir (d, verbosity)); @@ -225,7 +228,7 @@ namespace build2 // path p (d / n); if (r || !exists (p)) - touch (p, true /* create */, verbosity); + touch (ctx, p, true /* create */, verbosity); return r; } @@ -253,7 +256,10 @@ namespace build2 } fs_status - rmdir_buildignore (const dir_path& d, const path& n, uint16_t verbosity) + rmdir_buildignore (context& ctx, + const dir_path& d, + const path& n, + uint16_t verbosity) { // We should remove the .buildignore file only if the subsequent rmdir() // will succeed. In other words if the directory stays after the function @@ -263,12 +269,12 @@ namespace build2 // path p (d / n); if (exists (p) && empty_buildignore (d, n) && !work.sub (d)) - rmfile (p, verbosity); + rmfile (ctx, p, verbosity); // Note that in case of a system error the directory is likely to stay with // the .buildignore file already removed. Trying to restore it feels like // an overkill here. // - return rmdir (d, verbosity); + return rmdir (ctx, d, verbosity); } } diff --git a/libbuild2/filesystem.hxx b/libbuild2/filesystem.hxx index 6dca528..e7b3094 100644 --- a/libbuild2/filesystem.hxx +++ b/libbuild2/filesystem.hxx @@ -10,6 +10,8 @@ #include #include +#include + #include // Higher-level filesystem utilities built on top of . @@ -43,7 +45,7 @@ namespace build2 // create it and fail otherwise. // LIBBUILD2_SYMEXPORT void - touch (const path&, bool create, uint16_t verbosity = 1); + touch (context&, const path&, bool create, uint16_t verbosity = 1); // Return the modification time for an existing regular file and // timestamp_nonexistent otherwise. Print the diagnostics and fail on system @@ -88,22 +90,19 @@ namespace build2 fs_status rmfile (const path&, const T& target, uint16_t verbosity = 1); - inline fs_status - rmfile (const path& f, int verbosity = 1) // Literal overload (int). - { - return rmfile (f, f, static_cast (verbosity)); - } + fs_status + rmfile (context&, const path&, uint16_t verbosity = 1); - inline fs_status - rmfile (const path& f, uint16_t verbosity) // Overload (verb_never). - { - return rmfile (f, f, verbosity); - } + fs_status + rmfile (const path&, int = 1) = delete; + + fs_status + rmfile (const path&, uint16_t) = delete; // Similar to rmfile() but for symlinks. // LIBBUILD2_SYMEXPORT fs_status - rmsymlink (const path&, bool dir, uint16_t verbosity); + rmsymlink (context&, const path&, bool dir, uint16_t verbosity); // Similar to rmfile() but for directories (note: not -r). // @@ -113,27 +112,26 @@ namespace build2 fs_status rmdir (const dir_path&, const T& target, uint16_t verbosity = 1); - inline fs_status - rmdir (const dir_path& d, int verbosity = 1) // Literal overload (int). - { - return rmdir (d, d, static_cast (verbosity)); - } + fs_status + rmdir (context&, const dir_path&, uint16_t verbosity = 1); - inline fs_status - rmdir (const dir_path& d, uint16_t verbosity) // Overload (verb_never). - { - return rmdir (d, d, verbosity); - } + fs_status + rmdir (const dir_path&, int = 1) = delete; + + fs_status + rmdir (const dir_path&, uint16_t) = delete; // Remove the directory recursively (unless dry-run) and print the standard // diagnostics starting from the specified verbosity level. Note that this // function returns not_empty if we try to remove a working directory. If // the dir argument is false, then the directory itself is not removed. // - // @@ Collides (via ADL) with butl::rmdir_r(), which sucks. - // LIBBUILD2_SYMEXPORT fs_status - rmdir_r (const dir_path&, bool dir = true, uint16_t verbosity = 1); + rmdir_r (context& ctx, + const dir_path&, bool dir = true, uint16_t verbosity = 1); + + fs_status + rmdir_r (const dir_path&, bool = true, uint16_t = 1) = delete; // Check for a file, directory or filesystem entry existence. Print the // diagnostics and fail on system error, unless ignore_error is true. @@ -163,7 +161,8 @@ namespace build2 // Create a directory containing an empty .buildignore file. // LIBBUILD2_SYMEXPORT fs_status - mkdir_buildignore (const dir_path&, const path&, uint16_t verbosity = 1); + mkdir_buildignore (context&, + const dir_path&, const path&, uint16_t verbosity = 1); // Return true if the directory is empty or only contains the .buildignore // file. Fail if the directory doesn't exist. @@ -174,9 +173,11 @@ namespace build2 // Remove a directory if it is empty or only contains the .buildignore file. // LIBBUILD2_SYMEXPORT fs_status - rmdir_buildignore (const dir_path&, const path&, uint16_t verbosity = 1); + rmdir_buildignore (context&, + const dir_path&, const path&, uint16_t verbosity = 1); } +#include #include #endif // LIBBUILD2_FILESYSTEM_HXX diff --git a/libbuild2/filesystem.ixx b/libbuild2/filesystem.ixx new file mode 100644 index 0000000..6dab3ad --- /dev/null +++ b/libbuild2/filesystem.ixx @@ -0,0 +1,40 @@ +// file : libbuild2/filesystem.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +namespace build2 +{ + template + fs_status + rmfile (context&, const path&, const T&, uint16_t); + + template + inline fs_status + rmfile (const path& f, const T& t, uint16_t v) + { + return rmfile (t.ctx, f, t, v); + } + + inline fs_status + rmfile (context& ctx, const path& f, uint16_t v) + { + return rmfile (ctx, f, f, v); + } + + template + fs_status + rmdir (context&, const dir_path&, const T&, uint16_t); + + template + inline fs_status + rmdir (const dir_path& d, const T& t, uint16_t v) + { + return rmdir (t.ctx, d, t, v); + } + + inline fs_status + rmdir (context& ctx, const dir_path& d, uint16_t v) + { + return rmdir (ctx, d, d, v); + } +} diff --git a/libbuild2/filesystem.txx b/libbuild2/filesystem.txx index 057975a..e3cdef3 100644 --- a/libbuild2/filesystem.txx +++ b/libbuild2/filesystem.txx @@ -4,14 +4,13 @@ #include // is_base_of -#include // dry_run #include namespace build2 { template fs_status - rmfile (const path& f, const T& t, uint16_t v) + rmfile (context& ctx, const path& f, const T& t, uint16_t v) { using namespace butl; @@ -34,7 +33,7 @@ namespace build2 try { - rs = dry_run + rs = ctx.dry_run ? file_exists (f) ? rmfile_status::success : rmfile_status::not_exist : try_rmfile (f); } @@ -52,7 +51,7 @@ namespace build2 template fs_status - rmdir (const dir_path& d, const T& t, uint16_t v) + rmdir (context& ctx, const dir_path& d, const T& t, uint16_t v) { using namespace butl; @@ -75,7 +74,7 @@ namespace build2 rmdir_status rs; try { - rs = dry_run + rs = ctx.dry_run ? dir_exists (d) ? rmdir_status::success : rmdir_status::not_exist : !(w = work.sub (d)) ? try_rmdir (d) : rmdir_status::not_empty; } diff --git a/libbuild2/install/rule.cxx b/libbuild2/install/rule.cxx index 478aadd..7cee10e 100644 --- a/libbuild2/install/rule.cxx +++ b/libbuild2/install/rule.cxx @@ -698,6 +698,8 @@ namespace build2 const dir_path& d, bool verbose = true) { + context& ctx (rs.ctx); + // Here is the problem: if this is a dry-run, then we will keep showing // the same directory creation commands over and over again (because we // don't actually create them). There are two alternative ways to solve @@ -708,7 +710,7 @@ namespace build2 // with uninstall since the directories won't be empty (because we don't // actually uninstall any files). // - if (dry_run) + if (ctx.dry_run) return; dir_path chd (chroot_path (rs, d)); @@ -741,7 +743,7 @@ namespace build2 cstrings args; string reld ( - cast (rs.ctx.global_scope["build.host.class"]) == "windows" + cast (ctx.global_scope["build.host.class"]) == "windows" ? msys_path (chd) : relative (chd).string ()); @@ -780,12 +782,14 @@ namespace build2 const path& f, bool verbose) { + context& ctx (rs.ctx); + path relf (relative (f)); dir_path chd (chroot_path (rs, base.dir)); string reld ( - cast (rs.ctx.global_scope["build.host.class"]) == "windows" + cast (ctx.global_scope["build.host.class"]) == "windows" ? msys_path (chd) : relative (chd).string ()); @@ -818,7 +822,7 @@ namespace build2 else if (verb && verbose) text << "install " << t; - if (!dry_run) + if (!ctx.dry_run) run (pp, args); } @@ -829,6 +833,8 @@ namespace build2 const path& link, uint16_t verbosity) { + context& ctx (rs.ctx); + path rell (relative (chroot_path (rs, base.dir))); rell /= link; @@ -859,7 +865,7 @@ namespace build2 text << "install " << rell << " -> " << target; } - if (!dry_run) + if (!ctx.dry_run) run (pp, args); #else // The -f part. @@ -877,7 +883,7 @@ namespace build2 text << "install " << rell << " -> " << target; } - if (!dry_run) + if (!ctx.dry_run) try { // We have to go the roundabout way by adding directory to the target @@ -1014,7 +1020,7 @@ namespace build2 { // See install_d() for the rationale. // - if (dry_run) + if (rs.ctx.dry_run) return false; dir_path chd (chroot_path (rs, d)); @@ -1150,7 +1156,7 @@ namespace build2 if (verb >= verbosity && verb >= 2) text << "rm " << relf; - if (!dry_run) + if (!rs.ctx.dry_run) { try { @@ -1179,7 +1185,7 @@ namespace build2 if (verb >= verbosity && verb >= 2) print_process (args); - if (!dry_run) + if (!rs.ctx.dry_run) run (pp, args); } diff --git a/libbuild2/operation.cxx b/libbuild2/operation.cxx index ac1c159..080123e 100644 --- a/libbuild2/operation.cxx +++ b/libbuild2/operation.cxx @@ -283,7 +283,7 @@ namespace build2 // Set the dry-run flag. // - dry_run = dry_run_option; + ctx.dry_run = ctx.dry_run_option; // Setup progress reporting if requested. // @@ -356,7 +356,7 @@ namespace build2 // Clear the dry-run flag. // - dry_run = false; + ctx.dry_run = false; // Clear the progress if present. // diff --git a/libbuild2/test/rule.cxx b/libbuild2/test/rule.cxx index bd412f5..c0ece26 100644 --- a/libbuild2/test/rule.cxx +++ b/libbuild2/test/rule.cxx @@ -438,7 +438,7 @@ namespace build2 if (cast_false (rs.vars[ctx.var_forwarded])) { bl = bs.src_path () / wd.leaf (bs.out_path ()); - clean_backlink (bl, verb_never); + clean_backlink (ctx, bl, verb_never); } // If this is a (potentially) multi-testscript test, then create (and @@ -479,7 +479,7 @@ namespace build2 // Remove the directory itself not to confuse the runner which tries // to detect when tests stomp on each others feet. // - build2::rmdir_r (wd, true, 2); + rmdir_r (ctx, wd, true, 2); } // Delay actually creating the directory in case all the tests are @@ -491,7 +491,7 @@ namespace build2 // wait_guard wg; - if (!dry_run) + if (!ctx.dry_run) wg = wait_guard (ctx, ctx.count_busy (), t[a].task_count); // Result vector. @@ -516,11 +516,11 @@ namespace build2 // don't clean the existing one), we are going to ignore it for // dry-run. // - if (!dry_run) + if (!ctx.dry_run) { if (mk) { - mkdir_buildignore (wd, buildignore_file, 2); + mkdir_buildignore (ctx, wd, buildignore_file, 2); mk = false; } } @@ -534,9 +534,11 @@ namespace build2 dr << ' ' << t; } - res.push_back (dry_run ? scope_state::passed : scope_state::unknown); + res.push_back (ctx.dry_run + ? scope_state::passed + : scope_state::unknown); - if (!dry_run) + if (!ctx.dry_run) { scope_state& r (res.back ()); @@ -567,7 +569,7 @@ namespace build2 } } - if (!dry_run) + if (!ctx.dry_run) wg.wait (); // Re-examine. @@ -588,7 +590,7 @@ namespace build2 // Cleanup. // - if (!dry_run) + if (!ctx.dry_run) { if (!bad && !one && !mk && after == output_after::clean) { @@ -596,7 +598,7 @@ namespace build2 fail << "working directory " << wd << " is not empty at the " << "end of the test"; - rmdir_buildignore (wd, buildignore_file, 2); + rmdir_buildignore (ctx, wd, buildignore_file, 2); } } @@ -605,8 +607,10 @@ namespace build2 // If we dry-run then presumably all tests passed and we shouldn't // have anything left unless we are keeping the output. // - if (!bl.empty () && (dry_run ? after == output_after::keep : exists (wd))) - update_backlink (wd, bl, true /* changed */); + if (!bl.empty () && (ctx.dry_run + ? after == output_after::keep + : exists (wd))) + update_backlink (ctx, wd, bl, true /* changed */); if (bad) throw failed (); @@ -679,6 +683,8 @@ namespace build2 target_state rule:: perform_test (action a, const target& tt, size_t pass_n) const { + context& ctx (tt.ctx); + // First pass through. // if (pass_n != 0) @@ -779,7 +785,7 @@ namespace build2 cat = process (process_exit (0)); // Successfully exited. - if (!dry_run) + if (!ctx.dry_run) { try { @@ -800,7 +806,7 @@ namespace build2 // If dry-run, the target may not exist. // - process_path pp (!dry_run + process_path pp (!ctx.dry_run ? run_search (p, true /* init */) : try_run_search (p, true)); args.push_back (pp.empty () ? p.string ().c_str () : pp.recall_string ()); @@ -865,7 +871,7 @@ namespace build2 else if (verb) text << "test " << tt; - if (!dry_run) + if (!ctx.dry_run) { diag_record dr; if (!run_test (tt, diff --git a/libbuild2/test/script/runner.cxx b/libbuild2/test/script/runner.cxx index c27ad39..53f6741 100644 --- a/libbuild2/test/script/runner.cxx +++ b/libbuild2/test/script/runner.cxx @@ -699,6 +699,8 @@ namespace build2 void default_runner:: enter (scope& sp, const location&) { + context& ctx (sp.root.target_scope.ctx); + auto df = make_diag_frame ( [&sp](const diag_record& dr) { @@ -723,6 +725,7 @@ namespace build2 fs_status r ( sp.parent == nullptr ? mkdir_buildignore ( + ctx, sp.wd_path, sp.root.target_scope.root_scope ()->root_extra->buildignore_file, 2) @@ -744,6 +747,8 @@ namespace build2 void default_runner:: leave (scope& sp, const location& ll) { + context& ctx (sp.root.target_scope.ctx); + auto df = make_diag_frame ( [&sp](const diag_record& dr) { @@ -766,7 +771,7 @@ namespace build2 { // Remove the file if exists. Fail otherwise. // - if (rmfile (p, 3) == rmfile_status::not_exist) + if (rmfile (ctx, p, 3) == rmfile_status::not_exist) fail (ll) << "registered for cleanup special file " << p << " does not exist"; } @@ -800,9 +805,8 @@ namespace build2 { bool removed (false); - auto rm = [&cp, recursive, &removed, &sp, &ll] (path&& pe, - const string&, - bool interm) + auto rm = [&cp, recursive, &removed, &sp, &ll, &ctx] + (path&& pe, const string&, bool interm) { if (!interm) { @@ -819,7 +823,7 @@ namespace build2 if (!recursive) { - rmdir_status r (rmdir (d, 3)); + rmdir_status r (rmdir (ctx, d, 3)); if (r != rmdir_status::not_empty) return true; @@ -839,9 +843,7 @@ namespace build2 // Cast to uint16_t to avoid ambiguity with // libbutl::rmdir_r(). // - rmdir_status r (rmdir_r (d, - d != sp.wd_path, - static_cast (3))); + rmdir_status r (rmdir_r (ctx, d, d != sp.wd_path, 3)); if (r != rmdir_status::not_empty) return true; @@ -854,7 +856,7 @@ namespace build2 } } else - rmfile (pe, 3); + rmfile (ctx, pe, 3); } return true; @@ -921,13 +923,14 @@ namespace build2 // rmdir_status r ( recursive - ? rmdir_r (d, !wd, static_cast (v)) + ? rmdir_r (ctx, d, !wd, static_cast (v)) : (wd && sp.parent == nullptr ? rmdir_buildignore ( + ctx, d, sp.root.target_scope.root_scope ()->root_extra->buildignore_file, v) - : rmdir (d, v))); + : rmdir (ctx, d, v))); if (r == rmdir_status::success || (r == rmdir_status::not_exist && t == cleanup_type::maybe)) @@ -948,7 +951,7 @@ namespace build2 // Remove the file if exists. Fail otherwise. Removal of // non-existing file is not an error for 'maybe' cleanup type. // - if (rmfile (p, 3) == rmfile_status::not_exist && + if (rmfile (ctx, p, 3) == rmfile_status::not_exist && t == cleanup_type::always) fail (ll) << "registered for cleanup file " << p << " does not exist"; diff --git a/libbuild2/utility.cxx b/libbuild2/utility.cxx index d08cb31..ba50c5a 100644 --- a/libbuild2/utility.cxx +++ b/libbuild2/utility.cxx @@ -81,7 +81,6 @@ namespace build2 : (to_string (build_version.major ()) + '.' + to_string (build_version.minor ()))); - bool dry_run_option; optional mtime_check_option; optional config_sub; @@ -495,7 +494,6 @@ namespace build2 void init (void (*t) (bool), const char* a0, - bool dr, optional mc, optional cs, optional cg) @@ -504,7 +502,6 @@ namespace build2 argv0 = process::path_search (a0, true); - dry_run_option = dr; mtime_check_option = mc; config_sub = move (cs); diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index b5e842d..f7a437e 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -124,7 +124,6 @@ namespace build2 LIBBUILD2_SYMEXPORT void init (void (*terminate) (bool), const char* argv0, - bool dry_run = false, optional mtime_check = nullopt, optional config_sub = nullopt, optional config_guess = nullopt); @@ -143,8 +142,6 @@ namespace build2 LIBBUILD2_SYMEXPORT extern const standard_version build_version; LIBBUILD2_SYMEXPORT extern const string build_version_interface; - LIBBUILD2_SYMEXPORT extern bool dry_run_option; // --dry-run - // --[no-]mtime-check // LIBBUILD2_SYMEXPORT extern optional mtime_check_option; diff --git a/libbuild2/version/init.cxx b/libbuild2/version/init.cxx index 1c123bf..123dc65 100644 --- a/libbuild2/version/init.cxx +++ b/libbuild2/version/init.cxx @@ -374,7 +374,8 @@ namespace build2 // try { - auto_rmfile t (fixup_manifest (f, + auto_rmfile t (fixup_manifest (rs.ctx, + f, path::temp_path ("manifest"), m.version)); diff --git a/libbuild2/version/rule.cxx b/libbuild2/version/rule.cxx index 37e6b0f..fe999b3 100644 --- a/libbuild2/version/rule.cxx +++ b/libbuild2/version/rule.cxx @@ -328,7 +328,8 @@ namespace build2 // the out tree. Somehow the latter feels more appropriate (even though // if we crash in between, we won't clean it up). // - return fixup_manifest (p, rs.out_path () / "manifest.t", m.version); + return fixup_manifest ( + t.ctx, p, rs.out_path () / "manifest.t", m.version); } } } diff --git a/libbuild2/version/utility.cxx b/libbuild2/version/utility.cxx index 70daab1..0669da7 100644 --- a/libbuild2/version/utility.cxx +++ b/libbuild2/version/utility.cxx @@ -17,11 +17,14 @@ namespace build2 namespace version { auto_rmfile - fixup_manifest (const path& in, path out, const standard_version& v) + fixup_manifest (context& ctx, + const path& in, + path out, + const standard_version& v) { - auto_rmfile r (move (out), !dry_run /* active */); + auto_rmfile r (move (out), !ctx.dry_run /* active */); - if (!dry_run) + if (!ctx.dry_run) { try { diff --git a/libbuild2/version/utility.hxx b/libbuild2/version/utility.hxx index 16e8c78..170488d 100644 --- a/libbuild2/version/utility.hxx +++ b/libbuild2/version/utility.hxx @@ -8,6 +8,7 @@ #include #include +#include #include namespace build2 @@ -18,7 +19,10 @@ namespace build2 // not preserve comments. Probably acceptable for snapshots. // auto_rmfile - fixup_manifest (const path& in, path out, const standard_version&); + fixup_manifest (context&, + const path& in, + path out, + const standard_version&); } } -- cgit v1.1