From ad4120afce8c7bc4001fc0173a0ff7611ec0198d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 12 Dec 2015 13:46:07 +0200 Subject: Implement installation of prerequisite shared libraries --- build/algorithm.cxx | 2 +- build/algorithm.ixx | 4 +-- build/cxx/install | 2 +- build/cxx/install.cxx | 34 +++++++++++++++----- build/cxx/link | 22 +++++++++++++ build/cxx/link.cxx | 80 ++++++++++++++++++++++-------------------------- build/dist/operation.cxx | 2 +- build/file.cxx | 4 +-- build/install/rule | 10 ++++-- build/install/rule.cxx | 29 +++++++++--------- build/prerequisite | 7 ++--- build/prerequisite.cxx | 6 ++-- build/scope | 18 +++++++++-- build/target | 8 ++++- build/target.txx | 5 +-- 15 files changed, 142 insertions(+), 91 deletions(-) (limited to 'build') diff --git a/build/algorithm.cxx b/build/algorithm.cxx index 461d8d2..b296929 100644 --- a/build/algorithm.cxx +++ b/build/algorithm.cxx @@ -32,7 +32,7 @@ namespace build // If this is a project-qualified prerequisite, then this // is import's business. // - if (*pk.proj != nullptr) + if (pk.proj != nullptr) return import (pk); if (target* t = pk.tk.type->search (pk)) diff --git a/build/algorithm.ixx b/build/algorithm.ixx index 72a04fe..309f99b 100644 --- a/build/algorithm.ixx +++ b/build/algorithm.ixx @@ -34,10 +34,8 @@ namespace build const std::string* ext, scope* scope) { - const std::string* proj (nullptr); return search ( - prerequisite_key - {&proj, {&type, &dir, &name, &ext}, scope}); + prerequisite_key {nullptr, {&type, &dir, &name, &ext}, scope}); } template diff --git a/build/cxx/install b/build/cxx/install index 7117fb8..9df8408 100644 --- a/build/cxx/install +++ b/build/cxx/install @@ -15,7 +15,7 @@ namespace build class install: public build::install::file_rule { public: - virtual bool + virtual target* filter (action, target&, prerequisite_member) const; virtual match_result diff --git a/build/cxx/install.cxx b/build/cxx/install.cxx index e6417a6..f8c2c06 100644 --- a/build/cxx/install.cxx +++ b/build/cxx/install.cxx @@ -17,16 +17,35 @@ namespace build { using namespace bin; - bool install:: - filter (action, target& t, prerequisite_member p) const + target* install:: + filter (action a, target& t, prerequisite_member p) const { - // Don't install executable's prerequisite headers. + if (t.is_a ()) + { + // Don't install executable's prerequisite headers. + // + if (p.is_a () || p.is_a () || p.is_a () || p.is_a ()) + return nullptr; + } + + // If this is a shared library prerequisite, install it as long as it + // is in the same amalgamation as we are. // - if (t.is_a () && - (p.is_a () || p.is_a () || p.is_a () || p.is_a ())) - return false; + if ((t.is_a () || t.is_a ()) && + (p.is_a () || p.is_a ())) + { + target* pt (&p.search ()); + + // If this is the lib{} group, pick a member which we would link. + // + if (lib* l = pt->is_a ()) + pt = &link::link_member (*l, link::link_order (t)); - return true; + if (pt->is_a ()) // Can be liba{}. + return pt->in (t.weak_scope ()) ? pt : nullptr; + } + + return file_rule::filter (a, t, p); } match_result install:: @@ -39,7 +58,6 @@ namespace build // ones building this target. So first run link's match(). // match_result r (link::instance.match (a, t, hint)); - return r ? install::file_rule::match (a, t, "") : r; } diff --git a/build/cxx/link b/build/cxx/link index 5d3d29e..5a5aafa 100644 --- a/build/cxx/link +++ b/build/cxx/link @@ -12,6 +12,8 @@ #include #include +#include + namespace build { namespace cxx @@ -30,6 +32,26 @@ namespace build static link instance; + public: + enum class type {e, a, so}; + enum class order {a, so, a_so, so_a}; + + static type + link_type (target& t) + { + return t.is_a () + ? type::e + : (t.is_a () ? type::a : type::so); + } + + static order + link_order (target&); + + // Determine the library member (liba or libso) to link. + // + static target& + link_member (bin::lib&, order); + private: friend class compile; diff --git a/build/cxx/link.cxx b/build/cxx/link.cxx index f5a2e3d..9296035 100644 --- a/build/cxx/link.cxx +++ b/build/cxx/link.cxx @@ -38,16 +38,7 @@ namespace build { using namespace bin; - enum class type {e, a, so}; - enum class order {a, so, a_so, so_a}; - - static inline type - link_type (target& t) - { - return t.is_a () ? type::e : (t.is_a () ? type::a : type::so); - } - - static order + link::order link:: link_order (target& t) { const char* var; @@ -65,6 +56,40 @@ namespace build : v.size () > 1 && v[1] == "shared" ? order::a_so : order::a; } + target& link:: + link_member (bin::lib& l, order lo) + { + bool lso (true); + const string& at (as (*l["bin.lib"])); // Available types. + + switch (lo) + { + case order::a: + case order::a_so: + lso = false; // Fall through. + case order::so: + case order::so_a: + { + if (lso ? at == "static" : at == "shared") + { + if (lo == order::a_so || lo == order::so_a) + lso = !lso; + else + fail << (lso ? "shared" : "static") << " build of " << l + << " is not available"; + } + } + } + + target* r (lso ? static_cast (l.so) : l.a); + + if (r == nullptr) + r = &search (lso ? libso::static_type : liba::static_type, + prerequisite_key {nullptr, l.key (), nullptr}); + + return *r; + } + link::search_paths link:: extract_library_paths (scope& bs) { @@ -455,7 +480,7 @@ namespace build type lt (link_type (t)); bool so (lt == type::so); - optional lo; // Link-order. + order lo (link_order (t)); // Derive file name from target name. // @@ -541,38 +566,7 @@ namespace build } else if (lib* l = pt->is_a ()) { - // Determine the library type to link. - // - bool lso (true); - const string& at (as (*(*l)["bin.lib"])); - - if (!lo) - lo = link_order (t); - - switch (*lo) - { - case order::a: - case order::a_so: - lso = false; // Fall through. - case order::so: - case order::so_a: - { - if (lso ? at == "static" : at == "shared") - { - if (*lo == order::a_so || *lo == order::so_a) - lso = !lso; - else - fail << (lso ? "shared" : "static") << " build of " << *l - << " is not available"; - } - } - } - - pt = lso ? static_cast (l->so) : l->a; - - if (pt == nullptr) - pt = &search (lso ? libso::static_type : liba::static_type, - p.key ()); + pt = &link_member (*l, lo); } build::match (a, *pt); diff --git a/build/dist/operation.cxx b/build/dist/operation.cxx index 0069432..28a64fa 100644 --- a/build/dist/operation.cxx +++ b/build/dist/operation.cxx @@ -187,7 +187,7 @@ namespace build const dir_path& src_nroot (nrs.src_path ()); - if (!src_nroot.sub (src_root)) // Not a source-level amalgamation. + if (!src_nroot.sub (src_root)) // Not a strong amalgamation. continue; add_adhoc (src_nroot, "build/export.build"); diff --git a/build/file.cxx b/build/file.cxx index 59fe1b9..73ce7cb 100644 --- a/build/file.cxx +++ b/build/file.cxx @@ -957,8 +957,8 @@ namespace build target& import (const prerequisite_key& pk) { - assert (*pk.proj != nullptr); - const string& p (**pk.proj); + assert (pk.proj != nullptr); + const string& p (*pk.proj); // @@ We no longer have location. This is especially bad for the // empty case, i.e., where do I need to specify the project diff --git a/build/install/rule b/build/install/rule index af38587..54014e1 100644 --- a/build/install/rule +++ b/build/install/rule @@ -27,12 +27,16 @@ namespace build class file_rule: public rule { public: - virtual bool - filter (action, target&, prerequisite_member) const {return true;} - virtual match_result match (action, target&, const std::string&) const; + // Return NULL if this prerequisite should be ignored and pointer to its + // target otherwise. The default implementation ignores prerequsites that + // are outside of this target's project. + // + virtual target* + filter (action, target&, prerequisite_member) const; + virtual recipe apply (action, target&, const match_result&) const; diff --git a/build/install/rule.cxx b/build/install/rule.cxx index bd538ed..3f8c16a 100644 --- a/build/install/rule.cxx +++ b/build/install/rule.cxx @@ -100,6 +100,13 @@ namespace build return mr; } + target* file_rule:: + filter (action, target& t, prerequisite_member p) const + { + target& pt (p.search ()); + return pt.in (t.root_scope ()) ? &pt : nullptr; + } + recipe file_rule:: apply (action a, target& t, const match_result& mr) const { @@ -121,8 +128,6 @@ namespace build // run standard search_and_match()? Will need an indicator // that it was forced (e.g., [install]) for filter() below. // - scope& rs (t.root_scope ()); - for (prerequisite_member p: group_prerequisite_members (a, t)) { // Ignore unresolved targets that are imported from other projects. @@ -136,19 +141,13 @@ namespace build // Let a customized rule have its say. // - // @@ This will be skipped if forced with [install]. + // @@ This will be skipped if forced with [install]? // - if (!filter (a, t, p)) + target* pt (filter (a, t, p)); + if (pt == nullptr) continue; - target& pt (p.search ()); - - // Ignore targets that are outside of our project. - // - if (!pt.in (rs)) - continue; - - build::match (a, pt); + build::match (a, *pt); // If the matched rule returned noop_recipe, then the target // state will be set to unchanged as an optimization. Use this @@ -156,10 +155,10 @@ namespace build // will help a lot in case of any static installable content // (headers, documentation, etc). // - if (pt.state () != target_state::unchanged) - t.prerequisite_targets.push_back (&pt); + if (pt->state () != target_state::unchanged) + t.prerequisite_targets.push_back (pt); else - unmatch (a, pt); // No intent to execute. + unmatch (a, *pt); // No intent to execute. } // This is where we diverge depending on the operation. In the diff --git a/build/prerequisite b/build/prerequisite index 3022e44..c435fd3 100644 --- a/build/prerequisite +++ b/build/prerequisite @@ -30,8 +30,7 @@ namespace build public: typedef build::scope scope_type; - mutable const std::string* const* proj; // Only *proj can be NULL, points - // to project_name_pool. + mutable const std::string* proj; // Can be NULL, from project_name_pool. target_key tk; mutable scope_type* scope; // Can be NULL if tk.dir is absolute. }; @@ -43,7 +42,7 @@ namespace build // Can compare project name pointers since they are from project_name_pool. // - return *x.proj < *y.proj || (*x.proj == *y.proj && x.tk < y.tk); + return x.proj < y.proj || (x.proj == y.proj && x.tk < y.tk); } std::ostream& @@ -83,7 +82,7 @@ namespace build prerequisite_key key () const { - return prerequisite_key {&proj, {&type, &dir, &name, &ext}, &scope}; + return prerequisite_key {proj, {&type, &dir, &name, &ext}, &scope}; } public: diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx index 89ac275..49cab89 100644 --- a/build/prerequisite.cxx +++ b/build/prerequisite.cxx @@ -20,9 +20,9 @@ namespace build ostream& operator<< (ostream& os, const prerequisite_key& pk) { - if (*pk.proj != nullptr) - os << **pk.proj << '%'; - // + if (pk.proj != nullptr) + os << *pk.proj << '%'; + // Don't print scope if we are project-qualified or the // prerequisite's directory is absolute. In both these // cases the scope is not used to resolve it to target. diff --git a/build/scope b/build/scope index 551ce9d..0cb319f 100644 --- a/build/scope +++ b/build/scope @@ -39,6 +39,9 @@ namespace build const dir_path* out_path_ {nullptr}; const dir_path* src_path_ {nullptr}; + bool + root () const {return root_ == this;} + scope* parent_scope () const {return parent_;} @@ -64,8 +67,19 @@ namespace build : nullptr; } - bool - root () const {return root_ == this;} + // Root scope of the outermost amalgamation or NULL if this scope is not + // (yet) in any (known) project. If there is no amalgamation, then this + // function returns the root scope of the project (in other words, in this + // case a project is treated as its own amalgamation). + // + scope* + weak_scope () const + { + scope* r (root_); + if (r != nullptr) + for (; r->parent_->root_ != nullptr; r = r->parent_->root_) ; + return r; + } // Variables. // diff --git a/build/target b/build/target index 69f8f4f..22b5e89 100644 --- a/build/target +++ b/build/target @@ -229,6 +229,12 @@ namespace build scope& strong_scope () const {return *root_scope ().strong_scope ();} + // Root scope of the outermost amalgamation that contains this target. + // The same notes as to root_scope() apply. + // + scope& + weak_scope () const {return *root_scope ().weak_scope ();} + bool in (const scope& s) const @@ -517,7 +523,7 @@ namespace build key () const { return target != nullptr - ? prerequisite_key {&prerequisite.get ().proj, target->key (), nullptr} + ? prerequisite_key {prerequisite.get ().proj, target->key (), nullptr} : prerequisite.get ().key (); } diff --git a/build/target.txx b/build/target.txx index a63994b..192cfbc 100644 --- a/build/target.txx +++ b/build/target.txx @@ -47,10 +47,7 @@ namespace build if (tk.dir->absolute ()) dr << "target " << tk; else - { - const string* proj (nullptr); // Used for local prerequisites. - dr << "prerequisite " << prerequisite_key {&proj, tk, &s}; - } + dr << "prerequisite " << prerequisite_key {nullptr, tk, &s}; dr << info << "perhaps you forgot to add " << tk.type->name << "{*}: " << var << " = ..."; -- cgit v1.1