aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-08-03 15:47:35 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-08-03 15:47:35 +0200
commitabb7bf1045fde14f6ef87c8941ee22af233af397 (patch)
tree734b8e605c00f79e00687ec4912366b88fbbb050
parent0db17bfec8b3b6c436f3c9c346d17d98458c3654 (diff)
match_only rework, part 2
-rw-r--r--build/algorithm.cxx9
-rw-r--r--build/bin/rule.cxx71
-rw-r--r--build/bin/target3
-rw-r--r--build/bin/target.cxx9
-rw-r--r--build/cxx/compile.cxx43
-rw-r--r--build/cxx/link.cxx64
-rw-r--r--build/target14
-rw-r--r--build/target.cxx6
8 files changed, 123 insertions, 96 deletions
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index bf10b84..94c84b8 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -60,11 +60,12 @@ namespace build
{
pair<const rule*, match_result> r;
- // Clear the resolved targets list before calling match(). The rule
- // is free to, say, resize() this list in match() (provided that it
- // matches) in order to, for example, prepare it for apply().
+ // By default, clear the resolved targets list before calling
+ // match(). The rule is free to modify this list in match()
+ // (provided that it matches) in order to, for example, prepare
+ // it for apply().
//
- t.prerequisite_targets.clear ();
+ t.reset (a);
// If this is a nested operation, first try the outer operation.
// This allows a rule to implement a "precise match", that is,
diff --git a/build/bin/rule.cxx b/build/bin/rule.cxx
index 8df1ee0..8507497 100644
--- a/build/bin/rule.cxx
+++ b/build/bin/rule.cxx
@@ -37,44 +37,12 @@ namespace build
// members as our prerequisites.
//
match_result lib_rule::
- match (action a, target& t, const std::string&) const
- {
- // Search and match prerequisite libraries and add them to the
- // prerequisite targets. While we never execute this list
- // ourselves (see perform() below), this is necessary to make
- // the exported options machinery work for the library chains
- // (chaining is the reason why we have to do match, recursively).
- // See the cxx.export.*-related code in cxx/compile.cxx for
- // details.
- //
- for (prerequisite& p: group_prerequisites (t))
- {
- if (p.is_a<lib> ())
- {
- target& pt (search (p));
- match_only (a, pt);
- t.prerequisite_targets.push_back (&pt);
- }
- else if (p.is_a<liba> () || p.is_a<libso> ())
- {
- //@@ TMP: C++ link rule hasn't been converted to support
- // match_only().
- //
- target& pt (search (p));
- build::match (a, pt);
- t.prerequisite_targets.push_back (&pt);
- pt.dependents--; // No intent to execute.
- }
- }
-
- return t;
- }
-
- recipe lib_rule::
- apply (action a, target& xt, const match_result&) const
+ match (action a, target& xt, const std::string&) const
{
lib& t (static_cast<lib&> (xt));
+ // @@ 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_lib().
//
@@ -87,12 +55,20 @@ namespace build
fail << "unknown library type: " << type <<
info << "'static', 'shared', or 'both' expected";
+ // Search and pre-match the members. The pre-match here is part
+ // of the "library meta-information protocol" that could be used
+ // by the module that actually builds the members. The idea is
+ // that pre-matching members may populate our prerequisite_targets
+ // with prerequisite libraries from which others can extract the
+ // meta-information about the library, such as the options to use
+ // when linking it, etc.
+ //
if (ar)
{
if (t.a == nullptr)
t.a = &search<liba> (t.dir, t.name, t.ext, nullptr);
- build::match (a, *t.a);
+ match_only (a, *t.a);
}
if (so)
@@ -100,9 +76,30 @@ namespace build
if (t.so == nullptr)
t.so = &search<libso> (t.dir, t.name, t.ext, nullptr);
- build::match (a, *t.so);
+ match_only (a, *t.so);
}
+ return match_result (t, &type);
+ }
+
+ recipe lib_rule::
+ apply (action a, target& xt, const match_result& mr) const
+ {
+ lib& t (static_cast<lib&> (xt));
+
+ const string& type (*static_cast<const string*> (mr.cpvalue));
+
+ bool ar (type == "static" || type == "both");
+ bool so (type == "shared" || type == "both");
+
+ // Now we do full match.
+ //
+ if (ar)
+ build::match (a, *t.a);
+
+ if (so)
+ build::match (a, *t.so);
+
return &perform;
}
diff --git a/build/bin/target b/build/bin/target
index 946d7f6..729f119 100644
--- a/build/bin/target
+++ b/build/bin/target
@@ -86,6 +86,9 @@ namespace build
liba* a {nullptr};
libso* so {nullptr};
+ virtual void
+ reset (action_type);
+
public:
virtual const target_type& type () const {return static_type;}
static const target_type static_type;
diff --git a/build/bin/target.cxx b/build/bin/target.cxx
index 4e48d16..5a1bb92 100644
--- a/build/bin/target.cxx
+++ b/build/bin/target.cxx
@@ -154,6 +154,15 @@ namespace build
false
};
+ // lib
+ //
+ void lib::
+ reset (action_type)
+ {
+ // Don't clear prerequisite_targets since it is "given" to our
+ // members to implement "library meta-information protocol".
+ }
+
static target*
lib_factory (dir_path d, string n, const string* e)
{
diff --git a/build/cxx/compile.cxx b/build/cxx/compile.cxx
index 48d2240..8dcad1e 100644
--- a/build/cxx/compile.cxx
+++ b/build/cxx/compile.cxx
@@ -98,46 +98,23 @@ namespace build
// A dependency on a library is there so that we can get its
// cxx.export.poptions. In particular, making sure it is
// executed before us will only restrict parallelism. But we
- // do need to match it in order to get its prerequisite_targets
- // populated; see append_lib_options() above.
+ // do need to pre-match it in order to get its
+ // prerequisite_targets populated. This is the "library
+ // meta-information protocol". See also append_lib_options()
+ // above.
//
if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libso> ())
{
if (a.operation () == update_id)
{
- // Handle imported libraries.
+ // Handle imported libraries. We know that for such libraries
+ // we don't need to do match() in order to get options (if
+ // any, they would be set by search_library()).
//
- if (p.proj () != nullptr)
+ if (p.proj () == nullptr ||
+ link::search_library (lib_paths, p.prerequisite) == nullptr)
{
- // We know that for such libraries we don't need to do
- // match() in order to get options (if any, they would
- // be set by search_library()).
- //
- // @@ What if this is an installed import with an export
- // stub? How will such a stub capture prerequsite libs?
- // Probably as imported prerequisites, e.g., %lib{z}, not
- // as -lz in .libs variable.
- //
- // In fact, even if we tried match() on lib{}, nothing
- // would have matched since that lib{} is out of any
- // project and is not a file (which is the case for
- // liba/libso). So probably the importing code would
- // have to take care of importing all the prerequisite
- // libraries. It does make sense since we don't really
- // want to match a rule to an installed library.
- //
- if (link::search_library (lib_paths, p.prerequisite) != nullptr)
- continue;
- }
-
- target& pt (p.search ());
-
- if (p.is_a<lib> ()) //@@ TMP
- build::match_only (a, pt);
- else
- {
- build::match (a, pt);
- pt.dependents--;
+ match_only (a, p.search ());
}
}
diff --git a/build/cxx/link.cxx b/build/cxx/link.cxx
index 5c3a515..97c9696 100644
--- a/build/cxx/link.cxx
+++ b/build/cxx/link.cxx
@@ -365,7 +365,7 @@ namespace build
// (i.e., a utility library).
//
- bool so (t.is_a<libso> ());
+ type lt (link_type (t));
// Scan prerequisites and see if we can work with what we've got.
//
@@ -384,7 +384,7 @@ namespace build
}
else if (p.is_a<obja> ())
{
- if (so)
+ if (lt == type::so)
fail << "shared library " << t << " prerequisite " << p
<< " is static object";
@@ -423,6 +423,45 @@ namespace build
return nullptr;
}
+ // If we have any prerequisite libraries (which also means that
+ // we match), search/import and pre-match them to implement the
+ // "library meta-information protocol".
+ //
+ if (seen_lib && lt != type::e)
+ {
+ if (t.group != nullptr)
+ t.group->prerequisite_targets.clear (); // lib{}'s
+
+ search_paths_cache lib_paths; // Extract lazily.
+
+ for (prerequisite_member p: group_prerequisite_members (a, t))
+ {
+ if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libso> ())
+ {
+ target* pt (nullptr);
+
+ // Handle imported libraries.
+ //
+ if (p.proj () != nullptr)
+ pt = search_library (lib_paths, p.prerequisite);
+
+ if (pt == nullptr)
+ {
+ pt = &p.search ();
+ match_only (a, *pt);
+ }
+
+ // If the prerequisite came from the lib{} group, then also
+ // add it to lib's prerequisite_targets.
+ //
+ if (!p.prerequisite.belongs (t))
+ t.group->prerequisite_targets.push_back (pt);
+
+ t.prerequisite_targets.push_back (pt);
+ }
+ }
+ }
+
return seen_cxx || seen_c || seen_obj || seen_lib ? &t : nullptr;
}
@@ -449,6 +488,8 @@ namespace build
}
}
+ t.prerequisite_targets.clear (); // See lib pre-match in match() above.
+
// Inject dependency on the output directory.
//
inject_parent_fsdir (a, t);
@@ -480,27 +521,14 @@ namespace build
if (!p.is_a<c> () && !p.is_a<cxx> ())
{
- // Handle imported libraries. Essentially, we want to replicate
- // the -lfoo functionality but as part of our import support.
+ // Handle imported libraries.
//
- if (p.proj () != nullptr &&
- (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libso> ()))
- {
+ if (p.proj () != nullptr)
pt = search_library (lib_paths, p.prerequisite);
- // We only need this target if we are updating (remember, like
- // -lfoo). The question is why search in the first place? The
- // reason is the "not found" situation, in which someone else
- // (i.e., the import phase 2) could resolve it to something
- // that, who knows, might need cleaning, for example.
- //
- if (pt != nullptr && a.operation () != update_id)
- continue; // Skip.
- }
-
// The rest is the same basic logic as in search_and_match().
//
- if (pt == nullptr) // Could've been resolved by search_library().
+ if (pt == nullptr)
pt = &p.search ();
if (a.operation () == clean_id && !pt->dir.sub (*amlg))
diff --git a/build/target b/build/target
index dbfe9a8..2aa3b5f 100644
--- a/build/target
+++ b/build/target
@@ -140,6 +140,8 @@ namespace build
class target
{
public:
+ typedef build::action action_type;
+
virtual
~target () = default;
@@ -149,6 +151,12 @@ namespace build
target (dir_path d, std::string n, const std::string* e)
: dir (std::move (d)), name (std::move (n)), ext (e) {}
+ // Reset the target before matching a rule for it. The
+ // default implementation clears prerequisite_targets.
+ //
+ virtual void
+ reset (action_type);
+
const dir_path dir; // Absolute and normalized.
const std::string name;
const std::string* ext; // Extension, NULL means unspecified,
@@ -173,7 +181,7 @@ namespace build
// say that the rule "semantically recognizes" the group and picks
// some of its members.
//
- // Updating an alternative group as a whole can mean updating some
+ // Updating an alternatives group as a whole can mean updating some
// subset of its members (e.g., lib{}). Or the group may not support
// this at all (e.g., obj{}).
//
@@ -199,7 +207,7 @@ namespace build
// resolve_group_members() from <build/algorithm>.
//
virtual group_view
- group_members (action) const;
+ group_members (action_type) const;
target_key
key () const {return target_key {&type (), &dir, &name, &ext};}
@@ -318,8 +326,6 @@ namespace build
std::size_t dependents;
public:
- typedef build::action action_type;
-
action_type action; // Action this recipe is for.
public:
diff --git a/build/target.cxx b/build/target.cxx
index c1c5ef4..cf8747f 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -50,6 +50,12 @@ namespace build
// target
//
+ void target::
+ reset (action_type)
+ {
+ prerequisite_targets.clear ();
+ }
+
group_view target::
group_members (action_type) const
{