aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-02-14 12:32:59 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-02-14 14:53:51 +0200
commita523eb1b8b74a577e7ff0aa3fce4312acd4b3a75 (patch)
tree3cac12cad917eccfe8fffb57c7279f2d09bde0ca
parent8a7b3bb944ca08d240fc778a9269c6db0f9746f8 (diff)
Redo library meta-information protocol without match_only()
-rw-r--r--build2/algorithm26
-rw-r--r--build2/algorithm.ixx15
-rw-r--r--build2/bin/rule.cxx2
-rw-r--r--build2/cc/compile.cxx19
-rw-r--r--build2/cc/link.cxx123
-rw-r--r--build2/cc/utility3
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<const rule*, match_result>
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<recipe, action>
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<lib> ());
- // @@ 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<lib> () || p.is_a<liba> () || p.is_a<libs> ())
{
@@ -305,7 +302,17 @@ namespace build2
if (lib* l = pt->is_a<lib> ())
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<dir_paths> usr_lib_dirs; // Extract lazily.
-
- for (prerequisite_member p: group_prerequisite_members (ml, a, t))
- {
- if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> ())
- {
- 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<lib> ())
- 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<dir_paths> 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<c> ())
+ if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> ())
{
// 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<lib> ())
+ 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<lib> () || p.is_a<liba> () || p.is_a<libs> ())
+ continue; // Handled on pass 1.
+
+ if (!p.is_a (x_src) && !p.is_a<c> ())
+ {
+ 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<obj> ())
{
@@ -466,13 +463,9 @@ namespace build2
if (pt == nullptr)
pt = &search (ott, p.key ());
}
- else if (lib* l = pt->is_a<lib> ())
- {
- 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);