From a523eb1b8b74a577e7ff0aa3fce4312acd4b3a75 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 14 Feb 2017 12:32:59 +0200 Subject: Redo library meta-information protocol without match_only() --- build2/algorithm | 26 +++++------ build2/algorithm.ixx | 15 ++---- build2/bin/rule.cxx | 2 - build2/cc/compile.cxx | 19 +++++--- build2/cc/link.cxx | 123 ++++++++++++++++++++++++-------------------------- build2/cc/utility | 3 +- 6 files changed, 88 insertions(+), 100 deletions(-) diff --git a/build2/algorithm b/build2/algorithm index 18f2fce..d360fad 100644 --- a/build2/algorithm +++ b/build2/algorithm @@ -78,27 +78,23 @@ namespace build2 // Match and apply a rule to the action/target with ambiguity detection. // Increment the target's dependents count, which means that you should call - // this function with the intent to also call execute(). In case of - // optimizations that would avoid calling execute(), call unmatch() to - // indicate this. + // this function with the intent to also call execute(). // - void + // In case of optimizations that would avoid calling execute(), call + // unmatch() to indicate this. This is only allowed in the following + // cases: + // + // - target::unchanged() returns true + // - match() returns true + // + // Note that match() does not check unchanged(). + // + bool match (slock&, action, target&); - // Note that calling this function only makes sense if the target itself - // doesn't have its own dependents (since they will not be unmatched) or - // if you know for sure that someone else will match and execute this - // target for real. - // void unmatch (action, target&); - // Match (but do not apply) a rule to the action/target with ambiguity - // detection. Note that this function does not touch the dependents count. - // - void - match_only (slock&, action, target&); - // Match a "delegate rule" from withing another rules' apply() function // avoiding recursive matches (thus the third argument). Return recipe and // recipe action (if any). Note that unlike match(), this call doesn't diff --git a/build2/algorithm.ixx b/build2/algorithm.ixx index 6bc310f..6bbe4bb 100644 --- a/build2/algorithm.ixx +++ b/build2/algorithm.ixx @@ -63,7 +63,7 @@ namespace build2 pair match_impl (slock&, action, target&, bool apply, const rule* skip = nullptr); - inline void + inline bool match (slock& ml, action a, target& t) { assert (phase == run_phase::search_match); @@ -71,10 +71,12 @@ namespace build2 if (!t.recipe (a)) match_impl (ml, a, t, true); - t.dependents.fetch_add (1, std::memory_order_release); dependency_count.fetch_add (1, std::memory_order_release); + bool r (t.dependents++ != 0); // Safe if someone else is also a dependent. // text << "M " << t << ": " << t.dependents << " " << dependency_count; + + return r; } inline void @@ -94,15 +96,6 @@ namespace build2 #endif } - inline void - match_only (slock& ml, action a, target& t) - { - assert (phase == run_phase::search_match); - - if (!t.recipe (a)) - match_impl (ml, a, t, false); - } - inline pair match_delegate (slock& ml, action a, target& t, const rule& r) { diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx index 075af0e..446f91b 100644 --- a/build2/bin/rule.cxx +++ b/build2/bin/rule.cxx @@ -50,8 +50,6 @@ namespace build2 { lib& t (xt.as ()); - // @@ We have to re-query it on each match_only()! - // Get the library type to build. If not set for a target, this // should be configured at the project scope by init(). // diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx index 3039f00..c04f0a9 100644 --- a/build2/cc/compile.cxx +++ b/build2/cc/compile.cxx @@ -278,11 +278,8 @@ namespace build2 for (prerequisite_member p: group_prerequisite_members (ml, a, t)) { // A dependency on a library is there so that we can get its - // *.export.poptions. In particular, making sure it is executed before - // us will only restrict parallelism. But we do need to pre-match it - // in order to get its imports resolved and prerequisite_targets - // populated. This is the "library meta-information protocol". See - // also append_lib_options(). + // *.export.poptions. This is the "library meta-information + // protocol". See also append_lib_options(). // if (p.is_a () || p.is_a () || p.is_a ()) { @@ -305,7 +302,17 @@ namespace build2 if (lib* l = pt->is_a ()) pt = &link_member (*l, lo); - match_only (ml, a, *pt); + // Making sure it is executed before us will only restrict + // parallelism. But we do need to match it in order to get its + // imports resolved and prerequisite_targets populated. So we + // match it but then unmatch if it is safe. And thanks to the + // two-pass prerequisite search & match in link::apply() it will + // be safe unless someone is building an obj?{} target directory. + // + if (build2::match (ml, a, *pt)) + unmatch (a, *pt); + else + t.prerequisite_targets.push_back (pt); } continue; diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index 269b11f..682b736 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -124,53 +124,6 @@ namespace build2 return false; } - // Set the library type. - // - t.vars.assign (c_type) = string (x); //@@ move to apply()? - - // If we have any prerequisite libraries, search/import and pre-match - // them to implement the "library meta-information protocol". Don't do - // this if we are called from the install rule just to check if we would - // match. - // - auto op (a.operation ()); - auto oop (a.outer_operation ()); - - if (seen_lib && lt != otype::e && - op != install_id && oop != install_id && - op != uninstall_id && oop != uninstall_id) - { - const scope& bs (t.base_scope ()); - lorder lo (link_order (bs, lt)); - - optional usr_lib_dirs; // Extract lazily. - - for (prerequisite_member p: group_prerequisite_members (ml, a, t)) - { - if (p.is_a () || p.is_a () || p.is_a ()) - { - target* pt (nullptr); - - // Handle imported libraries. - // - if (p.proj ()) - pt = search_library (sys_lib_dirs, usr_lib_dirs, p.prerequisite); - - if (pt == nullptr) - { - pt = &p.search (); - - if (lib* l = pt->is_a ()) - pt = &link_member (*l, lo); - - match_only (ml, a, *pt); - } - - t.prerequisite_targets.push_back (pt); - } - } - } - return true; } @@ -328,6 +281,11 @@ namespace build2 otype lt (link_type (t)); lorder lo (link_order (bs, lt)); + // Set the library type (C, C++, etc). + // + if (lt != otype::e) + t.vars.assign (c_type) = string (x); + // Derive file name(s) and add ad hoc group members. // auto add_adhoc = [a, &bs] (target& t, const char* type) -> file& @@ -411,29 +369,35 @@ namespace build2 pdb.derive_path (t.path (), "pdb"); } - t.prerequisite_targets.clear (); // See lib pre-match in match() above. - // Inject dependency on the output directory. // inject_fsdir (ml, a, t); optional usr_lib_dirs; // Extract lazily. - // Process prerequisites: do rule chaining for C and X source files as - // well as search and match. + // Process prerequisites, pass 1: search and match prerequisite + // libraries. + // + // We do it first in order to indicate that we will execute these + // targets before matching any of the obj?{}. This makes it safe for + // compiler::apply() to unmatch them and therefore not to hinder + // parallelism. // - // When cleaning, ignore prerequisites that are not in the same or a + // When cleaning, we ignore prerequisites that are not in the same or a // subdirectory of our project root. // - const target_type& ott (lt == otype::e ? obje::static_type : - lt == otype::a ? obja::static_type : - objs::static_type); - + size_t slot (t.prerequisite_targets.size ()); // Start. for (prerequisite_member p: group_prerequisite_members (ml, a, t)) { + // We pre-allocate a NULL slot for each (potential; see clean) + // prerequisite target. + // + t.prerequisite_targets.push_back (nullptr); + const target*& cpt (t.prerequisite_targets.back ()); + target* pt (nullptr); - if (!p.is_a (x_src) && !p.is_a ()) + if (p.is_a () || p.is_a () || p.is_a ()) { // Handle imported libraries. // @@ -451,8 +415,41 @@ namespace build2 if (a.operation () == clean_id && !pt->dir.sub (rs.out_path ())) continue; // Skip. - // If this is the obj{} or lib{} target group, then pick the - // appropriate member and make sure it is searched and matched. + // If this is the lib{} target group, then pick the appropriate + // member. + // + if (lib* l = pt->is_a ()) + pt = &link_member (*l, lo); + + build2::match (ml, a, *pt); + cpt = pt; + } + } + + // Process prerequisites, pass 2: search and match obj{} amd do rule + // chaining for C and X source files. + // + const target_type& ott (lt == otype::e ? obje::static_type : + lt == otype::a ? obja::static_type : + objs::static_type); + + for (prerequisite_member p: group_prerequisite_members (ml, a, t)) + { + const target*& cpt (t.prerequisite_targets[slot++]); + target* pt (nullptr); + + if (p.is_a () || p.is_a () || p.is_a ()) + continue; // Handled on pass 1. + + if (!p.is_a (x_src) && !p.is_a ()) + { + pt = &p.search (); + + if (a.operation () == clean_id && !pt->dir.sub (rs.out_path ())) + continue; // Skip. + + // If this is the obj{} target group, then pick the appropriate + // member. // if (obj* o = pt->is_a ()) { @@ -466,13 +463,9 @@ namespace build2 if (pt == nullptr) pt = &search (ott, p.key ()); } - else if (lib* l = pt->is_a ()) - { - pt = &link_member (*l, lo); - } build2::match (ml, a, *pt); - t.prerequisite_targets.push_back (pt); + cpt = pt; continue; } @@ -642,7 +635,7 @@ namespace build2 build2::match (ml, a, *pt); } - t.prerequisite_targets.push_back (pt); + cpt = pt; } switch (a) diff --git a/build2/cc/utility b/build2/cc/utility index bb7ed34..b1d07b8 100644 --- a/build2/cc/utility +++ b/build2/cc/utility @@ -41,7 +41,8 @@ namespace build2 // Given the link order return the library member (liba or libs) to link. // - // Note that the const version assumes you have already called non-const. + // Note that the const version assumes you have already called non-const + // (which does the search, if necessary). // target& link_member (bin::lib&, lorder); -- cgit v1.1