From 32e04ad4b4a8dec07836b7c9fcf90fe72a006990 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 25 Aug 2018 17:40:21 +0200 Subject: Implement missing pieces in utility libraries support In particular, we can now build static libraries out of utility libraries. --- build2/bin/init.cxx | 189 ++++++++++++++++++++++++++------------------------ build2/bin/rule.cxx | 24 ++++--- build2/bin/rule.hxx | 12 ++++ build2/bin/target.cxx | 108 ++++++++++++++++++++++------- build2/bin/target.hxx | 24 ++++++- 5 files changed, 228 insertions(+), 129 deletions(-) (limited to 'build2/bin') diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx index 7e022fda..6d99e26 100644 --- a/build2/bin/init.cxx +++ b/build2/bin/init.cxx @@ -38,7 +38,7 @@ namespace build2 static const strings libs_lib {"shared", "static"}; bool - vars_init (scope& r, + vars_init (scope& rs, scope&, const location&, unique_ptr&, @@ -47,7 +47,7 @@ namespace build2 const variable_map&) { tracer trace ("bin::vars_init"); - l5 ([&]{trace << "for " << r.out_path ();}); + l5 ([&]{trace << "for " << rs.out_path ();}); assert (first); @@ -56,7 +56,7 @@ namespace build2 // Target is a string and not target_triplet because it can be // specified by the user. // - auto& vp (var_pool.rw (r)); + auto& vp (var_pool.rw (rs)); vp.insert ("config.bin.target", true); vp.insert ("config.bin.pattern", true); @@ -91,7 +91,7 @@ namespace build2 // example: // // exe{test}: liba{foo} - // liba{foo}: libu{foo1 foo2} + // liba{foo}: libua{foo1 foo2} // liba{foo}: bin.whole = false # Affects test but not foo1 and foo2. // // If unspecified, defaults to false for liba{} and to true for libux{}. @@ -121,6 +121,11 @@ namespace build2 tracer trace ("bin::config_init"); l5 ([&]{trace << "for " << bs.out_path ();}); + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "bin.config module must be loaded in project root"; + // Load bin.vars. // if (!cast_false (rs["bin.vars.loaded"])) @@ -149,7 +154,7 @@ namespace build2 // config.bin.lib // { - value& v (bs.assign ("bin.lib")); + value& v (rs.assign ("bin.lib")); if (!v) v = *required (rs, "config.bin.lib", "both").first; } @@ -157,7 +162,7 @@ namespace build2 // config.bin.exe.lib // { - value& v (bs.assign ("bin.exe.lib")); + value& v (rs.assign ("bin.exe.lib")); if (!v) v = *required (rs, "config.bin.exe.lib", exe_lib).first; } @@ -165,7 +170,7 @@ namespace build2 // config.bin.liba.lib // { - value& v (bs.assign ("bin.liba.lib")); + value& v (rs.assign ("bin.liba.lib")); if (!v) v = *required (rs, "config.bin.liba.lib", liba_lib).first; } @@ -173,7 +178,7 @@ namespace build2 // config.bin.libs.lib // { - value& v (bs.assign ("bin.libs.lib")); + value& v (rs.assign ("bin.libs.lib")); if (!v) v = *required (rs, "config.bin.libs.lib", libs_lib).first; } @@ -183,7 +188,7 @@ namespace build2 // This one is optional and we merge it into bin.rpath, if any. // See the cxx module for details on merging. // - bs.assign ("bin.rpath") += cast_null ( + rs.assign ("bin.rpath") += cast_null ( optional (rs, "config.bin.rpath")); // config.bin.{lib,exe}.{prefix,suffix} @@ -196,13 +201,13 @@ namespace build2 lookup p (omitted (rs, "config.bin.prefix").first); lookup s (omitted (rs, "config.bin.suffix").first); - auto set = [&rs, &bs] (const char* bv, const char* cv, lookup l) + auto set = [&rs] (const char* bv, const char* cv, lookup l) { if (lookup o = omitted (rs, cv).first) l = o; if (l) - bs.assign (bv) = *l; + rs.assign (bv) = *l; }; set ("bin.lib.prefix", "config.bin.lib.prefix", p); @@ -364,8 +369,8 @@ namespace build2 // Load bin.config. // - if (!cast_false (bs["bin.config.loaded"])) - load_module (rs, bs, "bin.config", loc, false, hints); + if (!cast_false (rs["bin.config.loaded"])) + load_module (rs, rs, "bin.config", loc, false, hints); // Cache some config values we will be needing below. // @@ -391,6 +396,7 @@ namespace build2 t.insert (); t.insert (); + t.insert (); t.insert (); t.insert (); t.insert (); @@ -466,6 +472,9 @@ namespace build2 r.insert (perform_update_id, "bin.libu", fail_); r.insert (perform_clean_id, "bin.libu", fail_); + r.insert (perform_update_id, "bin.libul", fail_); + r.insert (perform_clean_id, "bin.libul", fail_); + // Similar to alias. // @@ -493,8 +502,8 @@ namespace build2 } bool - ar_config_init (scope& r, - scope& b, + ar_config_init (scope& rs, + scope& bs, const location& loc, unique_ptr&, bool first, @@ -502,18 +511,18 @@ namespace build2 const variable_map& hints) { tracer trace ("bin::ar_config_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); // Make sure bin.config is loaded. // - if (!cast_false (b["bin.config.loaded"])) - load_module (r, b, "bin.config", loc, false, hints); + if (!cast_false (rs["bin.config.loaded"])) + load_module (rs, bs, "bin.config", loc, false, hints); // Enter configuration variables. // if (first) { - auto& v (var_pool.rw (r)); + auto& v (var_pool.rw (rs)); v.insert ("bin.rc.path"); v.insert ("bin.ranlib.path"); @@ -543,12 +552,12 @@ namespace build2 // Use the target to decide on the default binutils program names. // - const string& tsys (cast (r["bin.target.system"])); + const string& tsys (cast (rs["bin.target.system"])); const char* ar_d (tsys == "win32-msvc" ? "lib" : "ar"); // This can be either a pattern or a fallback search directory. // - const string* pat (cast_null (r["bin.pattern"])); + const string* pat (cast_null (rs["bin.pattern"])); bool fb (pat != nullptr && path::traits::is_separator (pat->back ())); // Don't save the default value to config.build so that if the user @@ -557,7 +566,7 @@ namespace build2 // auto ap ( config::required ( - r, + rs, "config.bin.ar", path (apply_pattern (ar_d, fb ? nullptr : pat)), false, @@ -565,7 +574,7 @@ namespace build2 auto rp ( config::required ( - r, + rs, "config.bin.ranlib", nullptr, false, @@ -585,7 +594,7 @@ namespace build2 diag_record dr (text); { - dr << "bin.ar " << project (r) << '@' << r.out_path () << '\n' + dr << "bin.ar " << project (rs) << '@' << rs.out_path () << '\n' << " ar " << ari.ar_path << '\n' << " id " << ari.ar_id << '\n' << " version " << ari.ar_version.string () << '\n' @@ -614,28 +623,28 @@ namespace build2 } } - r.assign ("bin.ar.path") = move (ari.ar_path); - r.assign ("bin.ar.id") = move (ari.ar_id); - r.assign ("bin.ar.signature") = move (ari.ar_signature); - r.assign ("bin.ar.checksum") = move (ari.ar_checksum); + rs.assign ("bin.ar.path") = move (ari.ar_path); + rs.assign ("bin.ar.id") = move (ari.ar_id); + rs.assign ("bin.ar.signature") = move (ari.ar_signature); + rs.assign ("bin.ar.checksum") = move (ari.ar_checksum); { semantic_version& v (ari.ar_version); - r.assign ("bin.ar.version") = v.string (); - r.assign ("bin.ar.version.major") = v.major; - r.assign ("bin.ar.version.minor") = v.minor; - r.assign ("bin.ar.version.patch") = v.patch; - r.assign ("bin.ar.version.build") = move (v.build); + rs.assign ("bin.ar.version") = v.string (); + rs.assign ("bin.ar.version.major") = v.major; + rs.assign ("bin.ar.version.minor") = v.minor; + rs.assign ("bin.ar.version.patch") = v.patch; + rs.assign ("bin.ar.version.build") = move (v.build); } if (ranlib != nullptr) { - r.assign ("bin.ranlib.path") = move (ari.ranlib_path); - r.assign ("bin.ranlib.id") = move (ari.ranlib_id); - r.assign ("bin.ranlib.signature") = + rs.assign ("bin.ranlib.path") = move (ari.ranlib_path); + rs.assign ("bin.ranlib.id") = move (ari.ranlib_id); + rs.assign ("bin.ranlib.signature") = move (ari.ranlib_signature); - r.assign ("bin.ranlib.checksum") = + rs.assign ("bin.ranlib.checksum") = move (ari.ranlib_checksum); } } @@ -644,8 +653,8 @@ namespace build2 } bool - ar_init (scope& r, - scope& b, + ar_init (scope& rs, + scope& bs, const location& loc, unique_ptr&, bool, @@ -653,22 +662,22 @@ namespace build2 const variable_map& hints) { tracer trace ("bin::ar_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); // Make sure the bin core and ar.config are loaded. // - if (!cast_false (b["bin.loaded"])) - load_module (r, b, "bin", loc, false, hints); + if (!cast_false (bs["bin.loaded"])) + load_module (rs, bs, "bin", loc, false, hints); - if (!cast_false (b["bin.ar.config.loaded"])) - load_module (r, b, "bin.ar.config", loc, false, hints); + if (!cast_false (bs["bin.ar.config.loaded"])) + load_module (rs, bs, "bin.ar.config", loc, false, hints); return true; } bool - ld_config_init (scope& r, - scope& b, + ld_config_init (scope& rs, + scope& bs, const location& loc, unique_ptr&, bool first, @@ -676,18 +685,18 @@ namespace build2 const variable_map& hints) { tracer trace ("bin::ld_config_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); // Make sure bin.config is loaded. // - if (!cast_false (b["bin.config.loaded"])) - load_module (r, b, "bin.config", loc, false, hints); + if (!cast_false (rs["bin.config.loaded"])) + load_module (rs, rs, "bin.config", loc, false, hints); // Enter configuration variables. // if (first) { - auto& v (var_pool.rw (r)); + auto& v (var_pool.rw (rs)); v.insert ("bin.ld.path"); v.insert ("config.bin.ld", true); @@ -701,17 +710,17 @@ namespace build2 // // Use the target to decide on the default ld name. // - const string& tsys (cast (r["bin.target.system"])); + const string& tsys (cast (rs["bin.target.system"])); const char* ld_d (tsys == "win32-msvc" ? "link" : "ld"); // This can be either a pattern or a fallback search directory. // - const string* pat (cast_null (r["bin.pattern"])); + const string* pat (cast_null (rs["bin.pattern"])); bool fb (pat != nullptr && path::traits::is_separator (pat->back ())); auto p ( config::required ( - r, + rs, "config.bin.ld", path (apply_pattern (ld_d, fb ? nullptr : pat)), false, @@ -725,25 +734,25 @@ namespace build2 // if (verb >= (p.second ? 2 : 3)) { - text << "bin.ld " << project (r) << '@' << r.out_path () << '\n' + text << "bin.ld " << project (rs) << '@' << rs.out_path () << '\n' << " ld " << ldi.path << '\n' << " id " << ldi.id << '\n' << " signature " << ldi.signature << '\n' << " checksum " << ldi.checksum; } - r.assign ("bin.ld.path") = move (ldi.path); - r.assign ("bin.ld.id") = move (ldi.id); - r.assign ("bin.ld.signature") = move (ldi.signature); - r.assign ("bin.ld.checksum") = move (ldi.checksum); + rs.assign ("bin.ld.path") = move (ldi.path); + rs.assign ("bin.ld.id") = move (ldi.id); + rs.assign ("bin.ld.signature") = move (ldi.signature); + rs.assign ("bin.ld.checksum") = move (ldi.checksum); } return true; } bool - ld_init (scope& r, - scope& b, + ld_init (scope& rs, + scope& bs, const location& loc, unique_ptr&, bool, @@ -751,17 +760,17 @@ namespace build2 const variable_map& hints) { tracer trace ("bin::ld_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); // Make sure the bin core and ld.config are loaded. // - if (!cast_false (b["bin.loaded"])) - load_module (r, b, "bin", loc, false, hints); + if (!cast_false (bs["bin.loaded"])) + load_module (rs, bs, "bin", loc, false, hints); - if (!cast_false (b["bin.ld.config.loaded"])) - load_module (r, b, "bin.ld.config", loc, false, hints); + if (!cast_false (bs["bin.ld.config.loaded"])) + load_module (rs, bs, "bin.ld.config", loc, false, hints); - const string& lid (cast (r["bin.ld.id"])); + const string& lid (cast (rs["bin.ld.id"])); // Register the pdb{} target if using the VC toolchain. // @@ -769,17 +778,17 @@ namespace build2 if (lid == "msvc") { - const target_type& pdb (b.derive_target_type ("pdb").first); - install_path (b, pdb, dir_path ("bin")); // Goes to install.bin - install_mode (b, pdb, "644"); // But not executable. + const target_type& pdb (bs.derive_target_type ("pdb").first); + install_path (bs, pdb, dir_path ("bin")); // Goes to install.bin + install_mode (bs, pdb, "644"); // But not executable. } return true; } bool - rc_config_init (scope& r, - scope& b, + rc_config_init (scope& rs, + scope& bs, const location& loc, unique_ptr&, bool first, @@ -787,18 +796,18 @@ namespace build2 const variable_map& hints) { tracer trace ("bin::rc_config_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); // Make sure bin.config is loaded. // - if (!cast_false (b["bin.config.loaded"])) - load_module (r, b, "bin.config", loc, false, hints); + if (!cast_false (bs["bin.config.loaded"])) + load_module (rs, bs, "bin.config", loc, false, hints); // Enter configuration variables. // if (first) { - auto& v (var_pool.rw (r)); + auto& v (var_pool.rw (rs)); v.insert ("bin.rc.path"); v.insert ("config.bin.rc", true); @@ -812,17 +821,17 @@ namespace build2 // // Use the target to decide on the default rc name. // - const string& tsys (cast (r["bin.target.system"])); + const string& tsys (cast (rs["bin.target.system"])); const char* rc_d (tsys == "win32-msvc" ? "rc" : "windres"); // This can be either a pattern or a fallback search directory. // - const string* pat (cast_null (r["bin.pattern"])); + const string* pat (cast_null (rs["bin.pattern"])); bool fb (pat != nullptr && path::traits::is_separator (pat->back ())); auto p ( config::required ( - r, + rs, "config.bin.rc", path (apply_pattern (rc_d, fb ? nullptr : pat)), false, @@ -836,25 +845,25 @@ namespace build2 // if (verb >= (p.second ? 2 : 3)) { - text << "bin.rc " << project (r) << '@' << r.out_path () << '\n' + text << "bin.rc " << project (rs) << '@' << rs.out_path () << '\n' << " rc " << rci.path << '\n' << " id " << rci.id << '\n' << " signature " << rci.signature << '\n' << " checksum " << rci.checksum; } - r.assign ("bin.rc.path") = move (rci.path); - r.assign ("bin.rc.id") = move (rci.id); - r.assign ("bin.rc.signature") = move (rci.signature); - r.assign ("bin.rc.checksum") = move (rci.checksum); + rs.assign ("bin.rc.path") = move (rci.path); + rs.assign ("bin.rc.id") = move (rci.id); + rs.assign ("bin.rc.signature") = move (rci.signature); + rs.assign ("bin.rc.checksum") = move (rci.checksum); } return true; } bool - rc_init (scope& r, - scope& b, + rc_init (scope& rs, + scope& bs, const location& loc, unique_ptr&, bool, @@ -862,15 +871,15 @@ namespace build2 const variable_map& hints) { tracer trace ("bin::rc_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); // Make sure the bin core and rc.config are loaded. // - if (!cast_false (b["bin.loaded"])) - load_module (r, b, "bin", loc, false, hints); + if (!cast_false (bs["bin.loaded"])) + load_module (rs, bs, "bin", loc, false, hints); - if (!cast_false (b["bin.rc.config.loaded"])) - load_module (r, b, "bin.rc.config", loc, false, hints); + if (!cast_false (bs["bin.rc.config.loaded"])) + load_module (rs, bs, "bin.rc.config", loc, false, hints); return true; } diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx index 9427697..b609c9a 100644 --- a/build2/bin/rule.cxx +++ b/build2/bin/rule.cxx @@ -37,15 +37,10 @@ namespace build2 // The whole logic is pretty much as if we had our two group members as // our prerequisites. // - bool lib_rule:: - match (action, target& xt, const string&) const + lib_rule::members lib_rule:: + build_members (const scope& rs) { - lib& t (xt.as ()); - - // Get the library type to build. If not set for a target, this should - // be configured at the project scope by init(). - // - const string& type (cast (t["bin.lib"])); + const string& type (cast (rs["bin.lib"])); bool a (type == "static" || type == "both"); bool s (type == "shared" || type == "both"); @@ -54,8 +49,17 @@ namespace build2 fail << "unknown library type: " << type << info << "'static', 'shared', or 'both' expected"; - t.a = a ? &search (t, t.dir, t.out, t.name) : nullptr; - t.s = s ? &search (t, t.dir, t.out, t.name) : nullptr; + return members {a, s}; + } + + bool lib_rule:: + match (action, target& xt, const string&) const + { + lib& t (xt.as ()); + + members bm (build_members (t.root_scope ())); + t.a = bm.a ? &search (t, t.dir, t.out, t.name) : nullptr; + t.s = bm.s ? &search (t, t.dir, t.out, t.name) : nullptr; return true; } diff --git a/build2/bin/rule.hxx b/build2/bin/rule.hxx index d656f62..ab8d64b 100644 --- a/build2/bin/rule.hxx +++ b/build2/bin/rule.hxx @@ -44,6 +44,18 @@ namespace build2 static target_state perform (action, const target&); + + // Return library types to build according to the bin.lib value (set + // on project's root scope by init()). + // + struct members + { + bool a; // static + bool s; // shared + }; + + static members + build_members (const scope&); }; } } diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx index 0560b8e..6c62258 100644 --- a/build2/bin/target.cxx +++ b/build2/bin/target.cxx @@ -71,7 +71,7 @@ namespace build2 // running serial. For the members it is also safe to set the group during // creation. - // obj*{}, bmi*{}, libu*{} member factory. + // obj*{} and bmi*{} member factory. // template static target* @@ -111,11 +111,11 @@ namespace build2 false }; - const target_type libue::static_type + const target_type obja::static_type { - "libue", - &libux::static_type, - &m_factory, + "obja", + &objx::static_type, + &m_factory, nullptr, /* fixed_extension */ &target_extension_var, &target_pattern_var, @@ -124,11 +124,11 @@ namespace build2 false }; - const target_type obja::static_type + const target_type bmia::static_type { - "obja", - &objx::static_type, - &m_factory, + "bmia", + &bmix::static_type, + &m_factory, nullptr, /* fixed_extension */ &target_extension_var, &target_pattern_var, @@ -137,11 +137,11 @@ namespace build2 false }; - const target_type bmia::static_type + const target_type objs::static_type { - "bmia", - &bmix::static_type, - &m_factory, + "objs", + &objx::static_type, + &m_factory, nullptr, /* fixed_extension */ &target_extension_var, &target_pattern_var, @@ -150,11 +150,11 @@ namespace build2 false }; - const target_type libua::static_type + const target_type bmis::static_type { - "libua", - &libux::static_type, - &m_factory, + "bmis", + &bmix::static_type, + &m_factory, nullptr, /* fixed_extension */ &target_extension_var, &target_pattern_var, @@ -163,11 +163,33 @@ namespace build2 false }; - const target_type objs::static_type + // libu*{} member factory. + // + template + static target* + libux_factory (const target_type&, dir_path dir, dir_path out, string n) { - "objs", - &objx::static_type, - &m_factory, + const target* g (targets.find (dir, out, n)); + + if (const target* g2 = targets.find (dir, out, n)) + { + if (g != 0) + fail << "both " << *g << " and " << g2 << " targets declared"; + + g = g2; + } + + M* m (new M (move (dir), move (out), move (n))); + m->group = g; + + return m; + } + + const target_type libue::static_type + { + "libue", + &libux::static_type, + &libux_factory, nullptr, /* fixed_extension */ &target_extension_var, &target_pattern_var, @@ -176,11 +198,12 @@ namespace build2 false }; - const target_type bmis::static_type + + const target_type libua::static_type { - "bmis", - &bmix::static_type, - &m_factory, + "libua", + &libux::static_type, + &libux_factory, nullptr, /* fixed_extension */ &target_extension_var, &target_pattern_var, @@ -193,7 +216,7 @@ namespace build2 { "libus", &libux::static_type, - &m_factory, + &libux_factory, nullptr, /* fixed_extension */ &target_extension_var, &target_pattern_var, @@ -255,6 +278,39 @@ namespace build2 false }; + // The same as g_factory() but without E. + // + static target* + libul_factory (const target_type&, dir_path dir, dir_path out, string n) + { + libua* a (phase == run_phase::load + ? const_cast (targets.find (dir, out, n)) + : nullptr); + libus* s (phase == run_phase::load + ? const_cast (targets.find (dir, out, n)) + : nullptr); + + libul* g (new libul (move (dir), move (out), move (n))); + + if (a != nullptr) a->group = g; + if (s != nullptr) s->group = g; + + return g; + } + + const target_type libul::static_type + { + "libul", + &libx::static_type, + &libul_factory, + nullptr, + nullptr, + nullptr, + nullptr, + &target_search, + false + }; + const target_type libu::static_type { "libu", diff --git a/build2/bin/target.hxx b/build2/bin/target.hxx index f0b427f..0bcdf90 100644 --- a/build2/bin/target.hxx +++ b/build2/bin/target.hxx @@ -135,7 +135,7 @@ namespace build2 virtual const target_type& dynamic_type () const {return static_type;} }; - // Common base for lib{} and libu{} groups. + // Common base for lib{} and libul{}/libu{} groups. // // We use mtime_target as a base for the "trust me it exists" functionality // which we use, for example, to have installed lib{} prerequisites that @@ -150,13 +150,21 @@ namespace build2 static const target_type static_type; }; - // The libu{} target group (utility library). + // The libul{}/libu{} target groups (utility library). // - // All the members are static libraries that differ base on the kind of + // All the members are static libraries that differ based on the kind of // object files they contains. Note that the group is more like obj{} // rather than lib{} in that one does not build the group directly rather // picking a suitable member. // + // libul{} is a "library utility library" in that the choice of members is + // libua{} or libus{}, even when linking an executable (normally a unit + // test). + // + // libu{} is a general utility library with all three types of members. It + // would normally be used when you want to build both a library from + // libua{}/libus{} and an executable from libue{}. + // class libux: public file // Common base of all libuX{} static libraries. { public: @@ -196,6 +204,16 @@ namespace build2 virtual const target_type& dynamic_type () const {return static_type;} }; + class libul: public libx + { + public: + using libx::libx; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + class libu: public libx { public: -- cgit v1.1