aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-05-13 08:24:25 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2020-05-27 08:35:29 +0200
commit44d3acdb065205f5fd69834a75b472721bea5443 (patch)
tree026c95b68088120b403140ab5f143c9ea114f90c
parent741cce26c1caeacc0e578a8bef1efefa993adcc1 (diff)
Support for cleaning up recipe builds
-rw-r--r--libbuild2/cc/init.cxx6
-rw-r--r--libbuild2/parser.cxx43
-rw-r--r--libbuild2/rule.cxx33
-rw-r--r--libbuild2/rule.hxx7
4 files changed, 83 insertions, 6 deletions
diff --git a/libbuild2/cc/init.cxx b/libbuild2/cc/init.cxx
index 2a0dbd2..07051c5 100644
--- a/libbuild2/cc/init.cxx
+++ b/libbuild2/cc/init.cxx
@@ -142,9 +142,9 @@ namespace build2
// Register scope operation callback.
//
- // It feels natural to do clean up sidebuilds as a post operation but
- // that prevents the (otherwise-empty) out root directory to be cleaned
- // up (via the standard fsdir{} chain).
+ // It feels natural to clean up sidebuilds as a post operation but that
+ // prevents the (otherwise-empty) out root directory to be cleaned up
+ // (via the standard fsdir{} chain).
//
rs.operation_callbacks.emplace (
perform_clean_id,
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index db95297..8d6beea 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -1021,9 +1021,12 @@ namespace build2
// ...
// }}
//
- // enter: percent or openining multi-curly-brace
+ // enter: start is percent or openining multi-curly-brace
// leave: token past newline after last closing multi-curly-brace
+ if (stage_ == stage::boot)
+ fail (t) << "ad hoc recipe specified during bootstrap";
+
// If we have a recipe, the target is not implied.
//
if (target_->implied)
@@ -1035,6 +1038,10 @@ namespace build2
default_target_ = target_;
}
+ // True if seen a recipe that requires cleanup.
+ //
+ bool clean (false);
+
for (token st (start);; st = t)
{
optional<string> diag;
@@ -1113,12 +1120,17 @@ namespace build2
location loc (get_location (st));
if (!lang)
+ {
ar.reset (new adhoc_script_rule (move (t.value),
move (diag),
loc,
st.value.size ()));
+ }
else if (*lang == "c++")
+ {
ar.reset (new adhoc_cxx_rule (move (t.value), loc, st.value.size ()));
+ clean = true;
+ }
else
fail (lloc) << "unknown recipe language '" << *lang << "'";
@@ -1134,6 +1146,35 @@ namespace build2
if (tt != type::percent && tt != type::multi_lcbrace)
break;
}
+
+ // If we have a recipe that needs cleanup, register an operation callback
+ // for this project unless it has already been done.
+ //
+ if (clean)
+ {
+ action a (perform_id, clean_id);
+ auto f (&adhoc_rule::clean_recipes_build);
+
+ // First check if we have already done this.
+ //
+ auto p (root_->operation_callbacks.equal_range (a));
+ for (; p.first != p.second; ++p.first)
+ {
+ auto t (
+ p.first->second.pre.target<scope::operation_callback::callback*> ());
+
+ if (t != nullptr && *t == f)
+ break;
+ }
+
+ // It feels natural to clean up recipe builds as a post operation but
+ // that prevents the (otherwise-empty) out root directory to be cleaned
+ // up (via the standard fsdir{} chain).
+ //
+ if (p.first == p.second)
+ root_->operation_callbacks.emplace (
+ a, scope::operation_callback {f, nullptr /*post*/});
+ }
}
void parser::
diff --git a/libbuild2/rule.cxx b/libbuild2/rule.cxx
index 98b4458..7286c63 100644
--- a/libbuild2/rule.cxx
+++ b/libbuild2/rule.cxx
@@ -305,6 +305,8 @@ namespace build2
// adhoc_rule
//
+ const dir_path adhoc_rule::recipes_build_dir ("recipes.out");
+
bool adhoc_rule::
match (action a, target& t, const string& h, optional<action> fallback) const
{
@@ -317,6 +319,35 @@ namespace build2
return true;
}
+ // Scope operation callback that cleans up recipe builds.
+ //
+ target_state adhoc_rule::
+ clean_recipes_build (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 / recipes_build_dir);
+
+ if (exists (d))
+ {
+ if (rmdir_r (ctx, d))
+ {
+ // Clean up 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 (ctx, d);
+
+ return target_state::changed;
+ }
+ }
+
+ return target_state::unchanged;
+ }
+
// adhoc_script_rule
//
void adhoc_script_rule::
@@ -572,8 +603,6 @@ namespace build2
<< ind << string (braces, '}');
}
- static const dir_path recipes_build_dir ("recipes.out");
-
// From module.cxx.
//
void
diff --git a/libbuild2/rule.hxx b/libbuild2/rule.hxx
index 69be2cc..ca17f2e 100644
--- a/libbuild2/rule.hxx
+++ b/libbuild2/rule.hxx
@@ -144,6 +144,13 @@ namespace build2
//
public:
build2::rule_match rule_match;
+
+ static const dir_path recipes_build_dir;
+
+ // Scope operation callback that cleans up ad hoc recipe builds.
+ //
+ static target_state
+ clean_recipes_build (action, const scope&, const dir&);
};
// Ad hoc script rule.