diff options
Diffstat (limited to 'libbuild2/adhoc-rule-cxx.cxx')
-rw-r--r-- | libbuild2/adhoc-rule-cxx.cxx | 155 |
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); } } |