From abb7bf1045fde14f6ef87c8941ee22af233af397 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 3 Aug 2015 15:47:35 +0200 Subject: match_only rework, part 2 --- build/algorithm.cxx | 9 ++++--- build/bin/rule.cxx | 71 ++++++++++++++++++++++++--------------------------- build/bin/target | 3 +++ build/bin/target.cxx | 9 +++++++ build/cxx/compile.cxx | 43 ++++++++----------------------- build/cxx/link.cxx | 64 +++++++++++++++++++++++++++++++++------------- build/target | 14 +++++++--- build/target.cxx | 6 +++++ 8 files changed, 123 insertions(+), 96 deletions(-) (limited to 'build') 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 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 ()) - { - target& pt (search (p)); - match_only (a, pt); - t.prerequisite_targets.push_back (&pt); - } - else if (p.is_a () || p.is_a ()) - { - //@@ 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 (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 (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 (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 (xt)); + + const string& type (*static_cast (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 () || p.is_a () || p.is_a ()) { 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 ()) //@@ 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 ()); + 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 ()) { - 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 () || p.is_a () || p.is_a ()) + { + 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 () && !p.is_a ()) { - // 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 () || p.is_a () || p.is_a ())) - { + 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 . // 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 { -- cgit v1.1