aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/adhoc-rule-cxx.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/adhoc-rule-cxx.cxx')
-rw-r--r--libbuild2/adhoc-rule-cxx.cxx155
1 files changed, 127 insertions, 28 deletions
diff --git a/libbuild2/adhoc-rule-cxx.cxx b/libbuild2/adhoc-rule-cxx.cxx
index 8d9b788..8a91809 100644
--- a/libbuild2/adhoc-rule-cxx.cxx
+++ b/libbuild2/adhoc-rule-cxx.cxx
@@ -3,13 +3,14 @@
#include <libbuild2/adhoc-rule-cxx.hxx>
-#include <libbutl/filesystem.mxx> // file_time()
+#include <libbutl/filesystem.hxx> // file_time()
#include <libbuild2/file.hxx>
#include <libbuild2/scope.hxx>
#include <libbuild2/target.hxx>
#include <libbuild2/context.hxx>
#include <libbuild2/algorithm.hxx>
+#include <libbuild2/filesystem.hxx>
#include <libbuild2/diagnostics.hxx>
using namespace butl;
@@ -19,16 +20,25 @@ namespace build2
// cxx_rule_v1
//
bool cxx_rule_v1::
- match (action, target&, const string&) const
+ match (action, target&) const
{
return true;
}
+ recipe cxx_rule_v1::
+ apply (action, target&) const
+ {
+ assert (false); // This (or the match_extra version) must be overriden.
+ return empty_recipe;
+ }
+
// adhoc_cxx_rule
//
adhoc_cxx_rule::
- adhoc_cxx_rule (const location& l, size_t b, uint64_t v, optional<string> s)
- : adhoc_rule ("<ad hoc c++ recipe>", l, b),
+ adhoc_cxx_rule (string n, const location& l, size_t b,
+ uint64_t v,
+ optional<string> s)
+ : adhoc_rule (move (n), l, b),
version (v),
separator (move (s)),
impl (nullptr)
@@ -38,7 +48,7 @@ namespace build2
}
bool adhoc_cxx_rule::
- recipe_text (context&, const target&, string&& t, attributes&)
+ recipe_text (const scope&, const target_type&, string&& t, attributes&)
{
code = move (t);
return true;
@@ -63,7 +73,7 @@ namespace build2
#if defined(BUILD2_BOOTSTRAP) || defined(LIBBUILD2_STATIC_BUILD)
bool adhoc_cxx_rule::
- match (action, target&, const string&) const
+ match (action, target&, const string&, match_extra&) const
{
// Note that we wait until match() (instead of, say, failing in the
// parser) to allow the presence of ad hoc C++ recipes for other
@@ -92,8 +102,13 @@ namespace build2
load_module_library (const path& lib, const string& sym, string& err);
bool adhoc_cxx_rule::
- match (action a, target& t, const string& hint) const
+ match (action a, target& xt, const string& hint, match_extra& me) const
{
+ const target& t (xt); // See adhoc_rule::match() for background.
+
+ if (pattern != nullptr && !pattern->match (a, t, hint, me))
+ return false;
+
tracer trace ("adhoc_cxx_rule::match");
context& ctx (t.ctx);
@@ -122,7 +137,8 @@ namespace build2
if ((impl = this->impl.load (memory_order_relaxed)) != nullptr)
break;
- using create_function = cxx_rule_v1* (const location&, target_state);
+ using create_function = cxx_rule_v1* (
+ const location&, target_state, const adhoc_rule_pattern*);
using load_function = create_function* ();
// The only way to guarantee that the name of our module matches its
@@ -290,11 +306,14 @@ namespace build2
create_module_context (ctx, loc);
}
- // "Switch" to the module context.
+ // Clear current project's environment and "switch" to the module
+ // context, including entering a scheduler sub-phase.
//
+ auto_thread_env penv (nullptr);
context& ctx (*t.ctx.module_context);
+ scheduler::phase_guard pg (*ctx.sched);
- const uint16_t verbosity (3); // Project creation command verbosity.
+ uint16_t verbosity (3); // Project creation command verbosity.
// Project and location signatures.
//
@@ -316,6 +335,17 @@ namespace build2
if (!create && (create = !check_sig (bf, psig)))
rmdir_r (ctx, pd, false, verbosity); // Never dry-run.
+ auto diag = [verbosity] (const path& f)
+ {
+ if (verb >= verbosity)
+ {
+ if (verb >= 2)
+ text << "cat >" << f;
+ else if (verb)
+ print_diag ("save", f);
+ }
+ };
+
path of;
ofdstream ofs;
@@ -328,6 +358,46 @@ namespace build2
// This way the configuration will be always in sync with ~build2
// and we can update the recipe manually (e.g., for debugging).
//
+ // Should we use ~build2 or ~build2-no-warnings? This case is similar
+ // to private host/module configurations in that the user doesn't have
+ // any control over the options used, etc. So it would be natural to
+ // use the no-warnings variant. However, unlike with tools/modules
+ // which can be configured in a user-created configuration (and which
+ // will normally be the case during development), for recipes it's
+ // always this automatically-create configuration. It feels like the
+ // best we can do is use ~build2-no-warnings by default but switch to
+ // ~build2 if the project is configured for development
+ // (config.<project>.develop).
+ //
+ string cfg;
+ {
+ const project_name& pn (named_project (rs));
+
+ if (!pn.empty ())
+ {
+ string var ("config." + pn.variable () + ".develop");
+
+ if (lookup l = rs[var])
+ {
+ // The value could be untyped if the project didn't declare this
+ // variable. Let's handle that case gracefully.
+ //
+ try
+ {
+ if (convert<bool> (*l))
+ cfg = "~build2";
+ }
+ catch (const invalid_argument& e)
+ {
+ fail << "invalid " << var << " value: " << e;
+ }
+ }
+ }
+
+ if (cfg.empty ())
+ cfg = "~build2-no-warnings";
+ }
+
create_project (
pd,
dir_path (), /* amalgamation */
@@ -336,7 +406,7 @@ namespace build2
{"cxx."}, /* root_modules */
"", /* root_post */
string ("config"), /* config_module */
- string ("config.config.load = ~build2"), /* config_file */
+ "config.config.load = " + cfg, /* config_file */
false, /* buildfile */
"build2 core", /* who */
verbosity); /* verbosity */
@@ -346,8 +416,7 @@ namespace build2
//
of = path (pd / "rule.cxx");
- if (verb >= verbosity)
- text << (verb >= 2 ? "cat >" : "save ") << of;
+ diag (of);
ofs.open (of);
@@ -367,6 +436,8 @@ namespace build2
<< "#include <libbuild2/depdb.hxx>" << '\n'
<< "#include <libbuild2/scope.hxx>" << '\n'
<< "#include <libbuild2/target.hxx>" << '\n'
+ << "#include <libbuild2/recipe.hxx>" << '\n'
+ << "#include <libbuild2/dyndep.hxx>" << '\n'
<< "#include <libbuild2/context.hxx>" << '\n'
<< "#include <libbuild2/variable.hxx>" << '\n'
<< "#include <libbuild2/algorithm.hxx>" << '\n'
@@ -430,9 +501,10 @@ namespace build2
// user-defined.
//
ofs << " static cxx_rule_v1*" << '\n'
- << " create_" << id << " (const location& l, target_state s)" << '\n'
+ << " create_" << id << " (const location& l, target_state s, " <<
+ "const adhoc_rule_pattern* p)" << '\n'
<< " {" << '\n'
- << " return new rule (l, s);" << '\n'
+ << " return new rule (l, s, p);" << '\n'
<< " }" << '\n'
<< '\n';
@@ -459,7 +531,8 @@ namespace build2
<< "#ifdef _WIN32" << '\n'
<< "__declspec(dllexport)" << '\n'
<< "#endif" << '\n'
- << "cxx_rule_v1* (*" << sym << " ()) (const location&, target_state)" << '\n'
+ << "cxx_rule_v1* (*" << sym << " ()) (const location&, " <<
+ "target_state, const adhoc_rule_pattern*)" << '\n'
<< "{" << '\n'
<< " return &rule_" << id << "::create_" << id << ";" << '\n'
<< "}" << '\n'
@@ -474,13 +547,12 @@ namespace build2
//
of = bf;
- if (verb >= verbosity)
- text << (verb >= 2 ? "cat >" : "save ") << of;
+ diag (of);
ofs.open (of);
- ofs << "import imp_libs += build2%lib{build2}" << '\n'
- << "libs{" << id << "}: cxx{rule} hxx{location} $imp_libs" << '\n'
+ ofs << "import impl_libs += build2%lib{build2}" << '\n'
+ << "libs{" << id << "}: cxx{rule} hxx{location} $impl_libs" << '\n'
<< '\n'
<< "if ($cxx.target.system == 'win32-msvc')" << '\n'
<< " cxx.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS" << '\n'
@@ -547,8 +619,7 @@ namespace build2
entry_time et (file_time (of));
- if (verb >= verbosity)
- text << (verb >= 2 ? "cat >" : "save ") << of;
+ diag (of);
ofs.open (of);
@@ -593,10 +664,10 @@ namespace build2
l = find_target ();
phase_switch mp (ctx, run_phase::match);
- if (build2::match (perform_update_id, *l) != target_state::unchanged)
+ if (match_sync (perform_update_id, *l) != target_state::unchanged)
{
phase_switch ep (ctx, run_phase::execute);
- execute (a, *l);
+ execute_sync (a, *l);
}
}
else
@@ -648,18 +719,46 @@ namespace build2
load_function* lf (function_cast<load_function*> (hs.second));
create_function* cf (lf ());
- impl = cf (loc, l->executed_state (perform_update_id));
+ impl = cf (loc, l->executed_state (perform_update_id), pattern);
this->impl.store (impl, memory_order_relaxed); // Still in load phase.
}
}
- return impl->match (a, t, hint);
+ return impl->match (a, xt, hint, me);
}
#endif // BUILD2_BOOTSTRAP || LIBBUILD2_STATIC_BUILD
recipe adhoc_cxx_rule::
- apply (action a, target& t) const
+ apply (action a, target& t, match_extra& me) const
+ {
+ // Handle matching explicit group member (see adhoc_rule::match() for
+ // background).
+ //
+ if (const group* g = (t.group != nullptr
+ ? t.group->is_a<group> ()
+ : nullptr))
+ {
+ // @@ Hm, this looks very similar to how we handle ad hoc group members.
+ // Shouldn't impl be given a chance to translate options or some
+ // such?
+ //
+ match_sync (a, *g, 0 /* options */);
+ return group_recipe; // Execute the group's recipe.
+ }
+
+ // Note that while we probably could call pattern's apply_group_members()
+ // here, apply_group_prerequisites() is normally called after adding
+ // prerequisites but before matching, which can only be done from the
+ // rule's implementation. Also, for apply_group_members(), there is the
+ // explicit group special case which may also require custom logic.
+ // So it feels best to leave both to the implementation.
+
+ return impl.load (memory_order_relaxed)->apply (a, t, me);
+ }
+
+ void adhoc_cxx_rule::
+ reapply (action a, target& t, match_extra& me) const
{
- return impl.load (memory_order_relaxed)->apply (a, t);
+ return impl.load (memory_order_relaxed)->reapply (a, t, me);
}
}