From ad720fabd468974e3909f62a0f4e4e3cf0d03aef Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 15 Apr 2015 11:59:58 +0200 Subject: Initial library support --- build/b.cxx | 7 +++- build/cxx/rule.cxx | 102 ++++++++++++++++++++++++++++++++++------------------- build/native | 12 ++++++- build/native.cxx | 17 ++++++--- build/rule.cxx | 21 +++-------- build/scope | 9 +++-- build/scope.cxx | 7 ---- build/target | 37 +++++++++++++++++-- build/target.cxx | 37 +++++++++++++++++++ build/variable | 16 ++++++--- build/variable.ixx | 11 ++++++ 11 files changed, 200 insertions(+), 76 deletions(-) (limited to 'build') diff --git a/build/b.cxx b/build/b.cxx index e53844c..3fe9d20 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -139,8 +139,9 @@ main (int argc, char* argv[]) target_types.insert (dir::static_type); target_types.insert (fsdir::static_type); - target_types.insert (exe::static_type); target_types.insert (obj::static_type); + target_types.insert (exe::static_type); + target_types.insert (lib::static_type); target_types.insert (cxx::h::static_type); target_types.insert (cxx::c::static_type); @@ -157,6 +158,10 @@ main (int argc, char* argv[]) rules[update_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link); rules[clean_id][typeid (exe)].emplace ("cxx.gnu.link", cxx_link); + rules[default_id][typeid (lib)].emplace ("cxx.gnu.link", cxx_link); + rules[update_id][typeid (lib)].emplace ("cxx.gnu.link", cxx_link); + rules[clean_id][typeid (lib)].emplace ("cxx.gnu.link", cxx_link); + cxx::compile cxx_compile; rules[default_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile); rules[update_id][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile); diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx index d8a7190..a7ec2a6 100644 --- a/build/cxx/rule.cxx +++ b/build/cxx/rule.cxx @@ -26,12 +26,15 @@ namespace build { namespace cxx { + // T is either target or scope. + // + template static void - append_options (vector& args, scope& s, const char* var) + append_options (vector& args, T& s, const char* var) { if (auto val = s[var]) { - for (const name& n: val.as ()) + for (const name& n: val.template as ()) { if (!n.type.empty () || !n.dir.empty ()) fail << "expected option instead of " << n << @@ -43,9 +46,9 @@ namespace build } static void - append_std (vector& args, scope& s, string& opt) + append_std (vector& args, target& t, string& opt) { - if (auto val = s["cxx.std"]) + if (auto val = t["cxx.std"]) { const string& v (val.as ()); @@ -95,7 +98,7 @@ namespace build obj& o (dynamic_cast (t)); if (o.path ().empty ()) - o.path (o.dir / path (o.name + ".o")); + o.path (o.derived_path ("o")); // Search and match all the existing prerequisites. The injection // code (below) takes care of the ones it is adding. @@ -182,14 +185,13 @@ namespace build { tracer trace ("cxx::compile::inject_prerequisites"); - scope& ts (o.base_scope ()); - scope& rs (*ts.root_scope ()); // Shouldn't have matched if nullptr. + scope& rs (*o.root_scope ()); // Shouldn't have matched if nullptr. const string& cxx (rs["config.cxx"].as ()); vector args {cxx.c_str ()}; append_options (args, rs, "config.cxx.poptions"); - append_options (args, ts, "cxx.poptions"); + append_options (args, o, "cxx.poptions"); // @@ Some C++ options (e.g., -std, -m) affect the preprocessor. // Or maybe they are not C++ options? Common options? @@ -197,9 +199,9 @@ namespace build append_options (args, rs, "config.cxx.coptions"); string std; // Storage. - append_std (args, ts, std); + append_std (args, o, std); - append_options (args, ts, "cxx.coptions"); + append_options (args, o, "cxx.coptions"); args.push_back ("-MM"); // @@ Change to -M args.push_back ("-MG"); // Treat missing headers as generated. @@ -268,7 +270,7 @@ namespace build // // Note that if the file has no extension, we record an empty // extension rather than NULL (which would signify that the - // extension needs to be added). + // default extension needs to be added). // dir_path d (f.directory ()); string n (f.leaf ().base ().string ()); @@ -337,21 +339,20 @@ namespace build path relo (relative (o.path ())); path rels (relative (s->path ())); - scope& ts (o.base_scope ()); - scope& rs (*ts.root_scope ()); // Shouldn't have matched if nullptr. + scope& rs (*o.root_scope ()); // Shouldn't have matched if nullptr. const string& cxx (rs["config.cxx"].as ()); vector args {cxx.c_str ()}; append_options (args, rs, "config.cxx.poptions"); - append_options (args, ts, "cxx.poptions"); + append_options (args, o, "cxx.poptions"); append_options (args, rs, "config.cxx.coptions"); string std; // Storage. - append_std (args, ts, std); + append_std (args, o, std); - append_options (args, ts, "cxx.coptions"); + append_options (args, o, "cxx.coptions"); args.push_back ("-o"); args.push_back (relo.string ().c_str ()); @@ -461,12 +462,23 @@ namespace build { tracer trace ("cxx::link::apply"); - // Derive executable file name from target name. + // Derive file name from target name. // - exe& e (dynamic_cast (t)); + bool so (false); + if (exe* e = dynamic_cast (&t)) + { + if (e->path ().empty ()) + e->path (e->derived_path ()); + } + else if (lib* l = dynamic_cast (&t)) + { + if (l->path ().empty ()) + l->path (l->derived_path ("so", "lib")); - if (e.path ().empty ()) - e.path (e.dir / path (e.name)); + so = true; + } + else + assert (false); // We may need the project roots for rule chaining (see below). // We will resolve them lazily only if needed. @@ -487,7 +499,7 @@ namespace build // target& pt (search (p)); - if (a.operation () == clean_id && !pt.dir.sub (e.dir)) + if (a.operation () == clean_id && !pt.dir.sub (t.dir)) p.target = nullptr; // Ignore. else build::match (a, pt); @@ -501,7 +513,7 @@ namespace build // but possible, the prerequisite is from a different project // altogether. So we are going to use the target's project. // - scope* rs (e.root_scope ()); + scope* rs (t.root_scope ()); assert (rs != nullptr); // Shouldn't have matched. out_root = &rs->path (); src_root = &rs->src_path (); @@ -550,7 +562,7 @@ namespace build // update will come and finish the rewrite process (it will even // reuse op that we have created but then ignored). So all is good. // - if (a.operation () == clean_id && !ot.dir.sub (e.dir)) + if (a.operation () == clean_id && !ot.dir.sub (t.dir)) { // If we shouldn't clean obj{}, then it is fair to assume // we shouldn't clean cxx{} either (generated source will @@ -561,6 +573,21 @@ namespace build continue; } + // Set the -fPIC option if we are building a shared object. + // + if (so) + { + auto var (ot.variables["cxx.coptions"]); + + if (!var) + { + if (auto var1 = ot.base_scope ()["cxx.coptions"]) + var = var1; + } + + var += "-fPIC"; + } + // If this target already exists, then it needs to be "compatible" // with what we are doing here. // @@ -633,42 +660,45 @@ namespace build } target_state link:: - perform_update (action a, target& t) + perform_update (action a, target& xt) { // @@ Q: // // - what are we doing with libraries? // + path_target& t (static_cast (xt)); - exe& e (dynamic_cast (t)); + //exe& e (dynamic_cast (t)); - if (!execute_prerequisites (a, e, e.mtime ())) + if (!execute_prerequisites (a, t, t.mtime ())) return target_state::unchanged; // Translate paths to relative (to working directory) ones. This // results in easier to read diagnostics. // - path rele (relative (e.path ())); + path relt (relative (t.path ())); vector relo; - scope& ts (e.base_scope ()); - scope& rs (*ts.root_scope ()); // Shouldn't have matched if nullptr. + scope& rs (*t.root_scope ()); // Shouldn't have matched if nullptr. const string& cxx (rs["config.cxx"].as ()); vector args {cxx.c_str ()}; + if (dynamic_cast (&t) != nullptr) + args.push_back ("-shared"); + append_options (args, rs, "config.cxx.coptions"); string std; // Storage. - append_std (args, ts, std); + append_std (args, t, std); - append_options (args, ts, "cxx.coptions"); + append_options (args, t, "cxx.coptions"); args.push_back ("-o"); - args.push_back (rele.string ().c_str ()); + args.push_back (relt.string ().c_str ()); append_options (args, rs, "config.cxx.loptions"); - append_options (args, ts, "cxx.loptions"); + append_options (args, t, "cxx.loptions"); for (const prerequisite& p: t.prerequisites) { @@ -680,14 +710,14 @@ namespace build } append_options (args, rs, "config.cxx.libs"); - append_options (args, ts, "cxx.libs"); + append_options (args, t, "cxx.libs"); args.push_back (nullptr); if (verb >= 1) print_process (args); else - text << "ld " << e; + text << "ld " << t; try { @@ -701,7 +731,7 @@ namespace build // current clock time. It has the advantage of having the // subseconds precision. // - e.mtime (system_clock::now ()); + t.mtime (system_clock::now ()); return target_state::changed; } catch (const process_error& e) diff --git a/build/native b/build/native index 6107705..88f340d 100644 --- a/build/native +++ b/build/native @@ -9,6 +9,16 @@ namespace build { + class obj: public file + { + public: + using file::file; + + public: + virtual const target_type& type () const {return static_type;} + static const target_type static_type; + }; + class exe: public file { public: @@ -19,7 +29,7 @@ namespace build static const target_type static_type; }; - class obj: public file + class lib: public file { public: using file::file; diff --git a/build/native.cxx b/build/native.cxx index 8d58e66..d8b0149 100644 --- a/build/native.cxx +++ b/build/native.cxx @@ -8,6 +8,15 @@ using namespace std; namespace build { + const target_type obj::static_type + { + typeid (obj), + "obj", + &file::static_type, + &target_factory, + file::static_type.search + }; + const target_type exe::static_type { typeid (exe), @@ -17,12 +26,12 @@ namespace build file::static_type.search }; - const target_type obj::static_type + const target_type lib::static_type { - typeid (obj), - "obj", + typeid (lib), + "lib", &file::static_type, - &target_factory, + &target_factory, file::static_type.search }; } diff --git a/build/rule.cxx b/build/rule.cxx index 413676b..884b28d 100644 --- a/build/rule.cxx +++ b/build/rule.cxx @@ -55,25 +55,14 @@ namespace build // path_target& pt (dynamic_cast (t)); + // Assign the path. While nromally we shouldn't do this in match(), + // no other rule should ever be ambiguous with the fallback one. + // if (pt.path ().empty ()) { - path p (t.dir / path (pt.name)); - - // @@ TMP: target name as an extension. - // - const string& e (pt.ext != nullptr ? *pt.ext : pt.type ().name); - - if (!e.empty ()) - { - p += '.'; - p += e; - } - - // While strictly speaking we shouldn't do this in match(), - // no other rule should ever be ambiguous with the fallback - // one. + // @@ TMP: using target name as the default extension. // - pt.path (move (p)); + pt.path (pt.derived_path (pt.type ().name)); } return pt.mtime () != timestamp_nonexistent ? &t : nullptr; diff --git a/build/scope b/build/scope index f9492e6..e4a8a7d 100644 --- a/build/scope +++ b/build/scope @@ -46,10 +46,13 @@ namespace build // public: value_proxy - operator[] (const std::string&); + operator[] (const variable&); value_proxy - operator[] (const variable&); + operator[] (const std::string& name) + { + return operator[] (variable_pool.find (name)); + } const dir_path* src_path_ {nullptr}; // Cached src_{root,base} var value. @@ -92,7 +95,7 @@ namespace build typedef dir_path_map::const_iterator iterator; - scope (): variables (*this) {} + scope (): variables (this) {} iterator i_; scope* parent_; diff --git a/build/scope.cxx b/build/scope.cxx index aced21b..6fb32b1 100644 --- a/build/scope.cxx +++ b/build/scope.cxx @@ -11,13 +11,6 @@ namespace build // scope // value_proxy scope:: - operator[] (const string& name) - { - const variable& var (variable_pool.find (name)); - return operator[] (var); - } - - value_proxy scope:: operator[] (const variable& var) { for (scope* s (this); s != nullptr; s = s->parent_scope ()) diff --git a/build/target b/build/target index dd62ec8..5495061 100644 --- a/build/target +++ b/build/target @@ -20,6 +20,7 @@ #include // map_iterator_adapter #include #include +#include #include #include #include // compare_*, extension_pool @@ -93,12 +94,13 @@ namespace build ~target () = default; target (dir_path d, std::string n, const std::string* e) - : dir (std::move (d)), name (std::move (n)), ext (e) {} + : dir (std::move (d)), name (std::move (n)), ext (e), + variables (nullptr) {} const dir_path dir; // Absolute and normalized. const std::string name; - const std::string* ext; // Extension, NULL means unspecified. - + const std::string* ext; // Extension, NULL means unspecified, + // empty means no extension. public: // Most qualified scope that contains this target. // @@ -112,6 +114,8 @@ namespace build scope* root_scope () const; + // Prerequisites. + // public: typedef std::vector> @@ -119,6 +123,25 @@ namespace build prerequisites_type prerequisites; + // Target-specific variables. + // + public: + variable_map variables; + + const variable_map& + ro_variables () const {return variables;} + + // Variable lookup in this target and all its outer scopes. + // + value_proxy + operator[] (const variable&); + + value_proxy + operator[] (const std::string& name) + { + return operator[] (variable_pool.find (name)); + } + public: target_state state; @@ -328,6 +351,14 @@ namespace build void path (path_type p) {assert (path_.empty ()); path_ = std::move (p);} + // Return a path derived from target's dir, name, and, if specified, + // ext. If ext is not specified, then use default_ext. If name_prefix + // if not NULL, add it before the name part. + // + path_type + derived_path (const char* default_ext = nullptr, + const char* name_prefix = nullptr); + protected: virtual timestamp load_mtime () const; diff --git a/build/target.cxx b/build/target.cxx index db02979..fbba1ed 100644 --- a/build/target.cxx +++ b/build/target.cxx @@ -51,6 +51,16 @@ namespace build return scopes.find (dir).root_scope (); } + value_proxy target:: + operator[] (const variable& var) + { + auto i (variables.find (var)); + + return i != variables.end () + ? value_proxy (&i->second, nullptr) + : base_scope ()[var]; + } + ostream& operator<< (ostream& os, const target& t) { @@ -232,6 +242,33 @@ namespace build // path_target // + path path_target:: + derived_path (const char* de, const char* np) + { + string n; + + if (np != nullptr) + n += np; + + n += name; + + if (ext != nullptr) + { + if (!ext->empty ()) + { + n += '.'; + n += *ext; + } + } + else if (de != nullptr) + { + n += '.'; + n += de; + } + + return dir / path_type (move (n)); + } + timestamp path_target:: load_mtime () const { diff --git a/build/variable b/build/variable index f994b58..6c14477 100644 --- a/build/variable +++ b/build/variable @@ -100,7 +100,7 @@ namespace build explicit operator bool () const {return defined () && !null ();} explicit operator value_ptr& () const {return *p;} - scope_type* scope; + scope_type* scope; // If NULL, then this is a target variable. // Get interface. See available specializations below. // @@ -119,6 +119,11 @@ namespace build const value_proxy& operator= (std::string) const; + // Append enother simple name to list_value. + // + const value_proxy& + operator+= (std::string) const; + const value_proxy& operator= (dir_path) const; @@ -218,7 +223,7 @@ namespace build value_proxy operator[] (const variable& v) { - return value_proxy (&base::operator[] (v), &scope_); + return value_proxy (&base::operator[] (v), scope_); } value_proxy @@ -234,7 +239,7 @@ namespace build return i != end () // @@ To do this properly we seem to need ro_value_proxy. // - ? value_proxy (&const_cast (i->second), &scope_) + ? value_proxy (&const_cast (i->second), scope_) : value_proxy (nullptr, nullptr); } @@ -251,10 +256,11 @@ namespace build } explicit - variable_map (scope& s): scope_ (s) {} + variable_map (scope* s): scope_ (s) {} private: - scope& scope_; // Scope to which this map belongs (and all its value). + scope* scope_; // Scope to which this map belongs or NULL if this + // is a target variable map. }; } diff --git a/build/variable.ixx b/build/variable.ixx index 2d52d9f..c0059c5 100644 --- a/build/variable.ixx +++ b/build/variable.ixx @@ -37,6 +37,17 @@ namespace build } inline const value_proxy& value_proxy:: + operator+= (std::string v) const + { + if (*p == nullptr) + *this = v; + else + as ().emplace_back (std::move (v)); + + return *this; + } + + inline const value_proxy& value_proxy:: operator= (dir_path v) const { p->reset (new list_value (std::move (v))); -- cgit v1.1