From 9d818423031083f227a5e872826ed8c2d6e14a0f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 27 Apr 2015 12:57:39 +0200 Subject: Add support for specifying library link order --- build/cxx/rule | 12 ++++- build/cxx/rule.cxx | 128 ++++++++++++++++++++++++++++++++++++++++------------- build/utility | 29 ++++++++++++ build/variable | 4 ++ build/variable.ixx | 9 +++- 5 files changed, 149 insertions(+), 33 deletions(-) (limited to 'build') diff --git a/build/cxx/rule b/build/cxx/rule index d139cc7..da46eb6 100644 --- a/build/cxx/rule +++ b/build/cxx/rule @@ -38,8 +38,6 @@ namespace build class link: public rule { public: - enum class type {exe, liba, libso}; - virtual void* match (action, target&, const std::string& hint) const; @@ -48,6 +46,16 @@ namespace build static target_state perform_update (action, target&); + + private: + enum class type {e, a, so}; + enum class order {a, so, a_so, so_a}; + + static type + link_type (target&); + + static order + link_order (target&); }; } } diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index 2d64701..ec2a0b9 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,8 @@ #include +#include + using namespace std; namespace build @@ -420,6 +423,67 @@ namespace build // link // + inline link::type link:: + link_type (target& t) + { + return t.is_a () ? type::e : (t.is_a () ? type::a : type::so); + } + + static const list_value default_exe_order (names {name ("shared"), + name ("static")}); + static const list_value default_liba_order ("static"); + static const list_value default_libso_order ("shared"); + + link::order link:: + link_order (target& t) + { + const char* var; + const char* cvar; + const list_value* plv; + + //@@ This should be in the bin module, not cxx! @@ remove config include. + // Maybe this should be triggered via the variable access? The same + // for bin.lib? After all, it can be queried in the buildfile. + // + switch (link_type (t)) + { + case type::e: + var = "bin.exe.lib"; + cvar = "config.bin.exe.lib"; + plv = &default_exe_order; + break; + case type::a: + var = "bin.liba.lib"; + cvar = "config.bin.liba.lib"; + plv = &default_liba_order; + break; + case type::so: + var = "bin.libso.lib"; + cvar = "config.bin.libso.lib"; + plv = &default_libso_order; + break; + } + + if (auto tv = t[var]) + plv = &tv.as (); + else + { + scope& root (*t.root_scope ()); + auto rv (root.vars.assign (var)); + rv = config::required (root, cvar, *plv).first; + plv = &rv.as (); + } + + //@@ Need to validate the value. Would be more efficient + // to do it once on assignment than every time on query. + // Custom var type? + // + const list_value& lv (*plv); + return lv[0].value == "shared" + ? lv.size () > 1 && lv[1].value == "static" ? order::so_a : order::so + : lv.size () > 1 && lv[1].value == "shared" ? order::a_so : order::a; + } + void* link:: match (action a, target& t, const string& hint) const { @@ -499,31 +563,19 @@ namespace build path_target& t (static_cast (xt)); - type tt (t.is_a () - ? type::exe - : (t.is_a () ? type::liba : type::libso)); - - bool so (tt == type::libso); // Obj-so. - - // Decide which lib{} member to use for this target. - // - bool lso; // Lib-so. - switch (tt) - { - case type::exe: lso = true; break; - case type::liba: lso = false; break; - case type::libso: lso = true; break; - } + type lt (link_type (t)); + bool so (lt == type::so); + optional lo; // Link-order. // Derive file name from target name. // if (t.path ().empty ()) { - switch (tt) + switch (lt) { - case type::exe: t.path (t.derived_path ( )); break; - case type::liba: t.path (t.derived_path ("a", "lib")); break; - case type::libso: t.path (t.derived_path ("so", "lib")); break; + case type::e: t.path (t.derived_path ( )); break; + case type::a: t.path (t.derived_path ("a", "lib")); break; + case type::so: t.path (t.derived_path ("so", "lib")); break; } } @@ -577,13 +629,32 @@ namespace build } else if (lib* l = pt->is_a ()) { - // Make sure the library build that we need is available. + // Determine the library type to link. // + bool lso (true); const string& at ((*l)["bin.lib"].as ()); - if (lso ? at == "static" : at == "shared") - fail << (lso ? "shared" : "static") << " build of " << *l - << " is not available"; + 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; @@ -769,11 +840,8 @@ namespace build { path_target& t (static_cast (xt)); - type tt (t.is_a () - ? type::exe - : (t.is_a () ? type::liba : type::libso)); - - bool so (tt == type::libso); + type lt (link_type (t)); + bool so (lt == type::so); if (!execute_prerequisites (a, t, t.mtime ())) return target_state::unchanged; @@ -788,7 +856,7 @@ namespace build vector args; string storage1; - if (tt == type::liba) + if (lt == type::a) { //@@ ranlib // @@ -832,7 +900,7 @@ namespace build args.push_back (relo.back ().string ().c_str ()); } - if (tt != type::liba) + if (lt != type::a) append_options (args, t, "cxx.libs"); args.push_back (nullptr); diff --git a/build/utility b/build/utility index 01ecd95..064a95f 100644 --- a/build/utility +++ b/build/utility @@ -38,6 +38,35 @@ namespace build bool operator() (const P& x, const P& y) const {return *x < *y;} }; + // Simple optional class template while waiting for std::optional. + // + template + class optional + { + public: + typedef T value_type; + + optional (): null_ (true) {} + optional (const T& v): value_ (v), null_ (false) {} + optional& operator= (const T& v) {value_ = v; null_ = false; return *this;} + + T& value () {return value_;} + const T& value () const {return value_;} + + T* operator-> () {return &value_;} + const T* operator-> () const {return &value_;} + + T& operator* () {return value_;} + const T& operator* () const {return value_;} + + explicit operator bool () const {return !null_;} + + private: + T value_; + bool null_; + }; + + // Support for reverse iteration using range-based for-loop: // // for (... : reverse_iterate (x)) ... diff --git a/build/variable b/build/variable index 05ca671..59db060 100644 --- a/build/variable +++ b/build/variable @@ -67,6 +67,7 @@ namespace build struct list_value: value, names { public: + list_value () = default; list_value (names d): names (std::move (d)) {} list_value (std::string d) {emplace_back (std::move (d));} list_value (dir_path d) {emplace_back (std::move (d));} @@ -115,6 +116,9 @@ namespace build operator= (const value_proxy&) const; const value_proxy& + operator= (list_value) const; + + const value_proxy& operator= (std::string) const; const value_proxy& diff --git a/build/variable.ixx b/build/variable.ixx index 52272cc..54d7104 100644 --- a/build/variable.ixx +++ b/build/variable.ixx @@ -26,6 +26,13 @@ namespace build } inline const value_proxy& value_proxy:: + operator= (list_value v) const + { + p->reset (new list_value (std::move (v))); + return *this; + } + + inline const value_proxy& value_proxy:: operator= (std::string v) const { // In most cases this is used to initialize a new variable, so @@ -70,7 +77,7 @@ namespace build operator+= (const list_value& v) const { if (*p == nullptr) - *this = value_ptr (new list_value (v)); + *this = v; else { list_value& lv (as ()); -- cgit v1.1