From bf959a7fc119f9156c4b84c9d0a10900d9153f8d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 27 Jul 2017 14:45:05 +0200 Subject: Initial infrastructure for utility libraries --- build2/bin/init.cxx | 27 ++- build2/bin/rule.cxx | 27 +-- build2/bin/rule.hxx | 18 +- build2/bin/target.cxx | 78 ++++++++- build2/bin/target.hxx | 71 +++++++- build2/cc/common.cxx | 42 +++-- build2/cc/common.hxx | 4 +- build2/cc/compile.cxx | 122 +++++++------ build2/cc/compile.hxx | 16 +- build2/cc/install.cxx | 2 +- build2/cc/link.cxx | 405 ++++++++++++++++++++++++++------------------ build2/cc/link.hxx | 12 +- build2/cc/module.cxx | 14 ++ build2/cc/types.hxx | 19 +++ build2/cc/utility.cxx | 58 ++++--- build2/cc/utility.hxx | 13 +- build2/cc/utility.ixx | 14 +- build2/cc/windows-rpath.cxx | 12 +- tests/cc/libu/buildfile | 8 + tests/cc/libu/testscript | 54 ++++++ 20 files changed, 675 insertions(+), 341 deletions(-) create mode 100644 tests/cc/libu/buildfile create mode 100644 tests/cc/libu/testscript diff --git a/build2/bin/init.cxx b/build2/bin/init.cxx index 71c580b..fb9dd71 100644 --- a/build2/bin/init.cxx +++ b/build2/bin/init.cxx @@ -24,8 +24,7 @@ namespace build2 { namespace bin { - static const obj_rule obj_; - static const bmi_rule bmi_; + static const fail_rule fail_; static const lib_rule lib_; // Default config.bin.*.lib values. @@ -368,10 +367,17 @@ namespace build2 t.insert (); t.insert (); + t.insert (); + t.insert (); + t.insert (); + t.insert (); + t.insert (); t.insert (); t.insert (); + // Note: libu*{} are not installable. + // if (install_loaded) { install_path (bs, dir_path ("lib")); // Install in install.lib. @@ -422,22 +428,25 @@ namespace build2 { auto& r (bs.rules); - r.insert (perform_update_id, "bin.obj", obj_); - r.insert (perform_clean_id, "bin.obj", obj_); + r.insert (perform_update_id, "bin.obj", fail_); + r.insert (perform_clean_id, "bin.obj", fail_); + + r.insert (perform_update_id, "bin.bmi", fail_); + r.insert (perform_clean_id, "bin.bmi", fail_); - r.insert (perform_update_id, "bin.bmi", bmi_); - r.insert (perform_clean_id, "bin.bmi", bmi_); + r.insert (perform_update_id, "bin.libu", fail_); + r.insert (perform_clean_id, "bin.libu", fail_); r.insert (perform_update_id, "bin.lib", lib_); - r.insert (perform_clean_id, "bin.lib", lib_); + r.insert (perform_clean_id, "bin.lib", lib_); - // Configure member. + // Configure members. // r.insert (configure_update_id, "bin.lib", lib_); if (install_loaded) { - r.insert (perform_install_id, "bin.lib", lib_); + r.insert (perform_install_id, "bin.lib", lib_); r.insert (perform_uninstall_id, "bin.lib", lib_); } } diff --git a/build2/bin/rule.cxx b/build2/bin/rule.cxx index b6e5a53..bb9036b 100644 --- a/build2/bin/rule.cxx +++ b/build2/bin/rule.cxx @@ -17,35 +17,22 @@ namespace build2 { namespace bin { - // obj + // fail_rule // - match_result obj_rule:: + match_result fail_rule:: match (action a, target& t, const string&) const { - fail << diag_doing (a, t) << " target group" << - info << "explicitly select obje{}, obja{}, or objs{} member"; - - return false; - } - - recipe obj_rule:: - apply (action, target&) const {return empty_recipe;} + const char* n (t.dynamic_type ().name); // Ignore derived type. - // bmi - // - match_result bmi_rule:: - match (action a, target& t, const string&) const - { fail << diag_doing (a, t) << " target group" << - info << "explicitly select bmie{}, bmia{}, or bmis{} member"; - - return false; + info << "explicitly select " << n << "e{}, " << n << "a{}, or " + << n << "s{} member" << endf; } - recipe bmi_rule:: + recipe fail_rule:: apply (action, target&) const {return empty_recipe;} - // lib + // lib_rule // // The whole logic is pretty much as if we had our two group members as // our prerequisites. diff --git a/build2/bin/rule.hxx b/build2/bin/rule.hxx index 4637479..b4835dc 100644 --- a/build2/bin/rule.hxx +++ b/build2/bin/rule.hxx @@ -14,22 +14,12 @@ namespace build2 { namespace bin { - class obj_rule: public rule + // Fail rule for obj{}, bmi{}, and libu{}. + // + class fail_rule: public rule { public: - obj_rule () {} - - virtual match_result - match (action, target&, const string&) const override; - - virtual recipe - apply (action, target&) const override; - }; - - class bmi_rule: public rule - { - public: - bmi_rule () {} + fail_rule () {} virtual match_result match (action, target&, const string&) const override; diff --git a/build2/bin/target.cxx b/build2/bin/target.cxx index f0e0152..aa37202 100644 --- a/build2/bin/target.cxx +++ b/build2/bin/target.cxx @@ -10,6 +10,30 @@ namespace build2 { namespace bin { + const target_type libx::static_type + { + "libx", + &target::static_type, + nullptr, + nullptr, + nullptr, + nullptr, + &target_search, + false + }; + + const target_type libux::static_type + { + "libux", + &file::static_type, + nullptr, + nullptr, + nullptr, + nullptr, + &target_search, + false + }; + // Note that we link groups during the load phase since this is often // relied upon when setting target-specific variables (e.g., we may set a // common value for lib{} and then append liba/libs-specific values to @@ -19,7 +43,7 @@ namespace build2 extern const char ext_var[] = "extension"; // VC14 rejects constexpr. - // obj*{} and bmi*{} member factory. + // obj*{}, bmi*{}, libu*{} member factory. // template static pair> @@ -61,6 +85,18 @@ namespace build2 false }; + const target_type libue::static_type + { + "libue", + &libux::static_type, + &m_factory, + &target_extension_var, + &target_pattern_var, + nullptr, + &target_search, // Note: not _file(); don't look for an existing file. + false + }; + const target_type obja::static_type { "obja", @@ -85,6 +121,18 @@ namespace build2 false }; + const target_type libua::static_type + { + "libua", + &libux::static_type, + &m_factory, + &target_extension_var, + &target_pattern_var, + nullptr, + &target_search, // Note: not _file(); don't look for an existing file. + false + }; + const target_type objs::static_type { "objs", @@ -109,7 +157,19 @@ namespace build2 false }; - // obj{} and bmi{} group factory. + const target_type libus::static_type + { + "libus", + &libux::static_type, + &m_factory, + &target_extension_var, + &target_pattern_var, + nullptr, + &target_search, // Note: not _file(); don't look for an existing file. + false + }; + + // obj{}, bmi{}, and libu{} group factory. // template static pair> @@ -164,6 +224,18 @@ namespace build2 false }; + const target_type libu::static_type + { + "libu", + &libx::static_type, + &g_factory, + nullptr, + nullptr, + nullptr, + &target_search, + false + }; + // What extensions should we use? At the outset, this is platform- // dependent. And if we consider cross-compilation, is it build or // host-dependent? Feels like it should be host-dependent so that @@ -238,7 +310,7 @@ namespace build2 const target_type lib::static_type { "lib", - &target::static_type, + &libx::static_type, &lib_factory, nullptr, nullptr, diff --git a/build2/bin/target.hxx b/build2/bin/target.hxx index 13d7596..e0c0358 100644 --- a/build2/bin/target.hxx +++ b/build2/bin/target.hxx @@ -117,6 +117,73 @@ namespace build2 virtual const target_type& dynamic_type () const {return static_type;} }; + // Common base for lib{} and libu{} groups. + // + class libx: public target + { + public: + using target::target; + + public: + static const target_type static_type; + }; + + // The libu{} target group (utility library). + // + // All the members are static libraries that differ base 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. + // + class libux: public file // Common base of all libuX{} static libraries. + { + public: + using file::file; + + public: + static const target_type static_type; + }; + + class libue: public libux + { + public: + using libux::libux; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + + class libua: public libux + { + public: + using libux::libux; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + + class libus: public libux + { + public: + using libux::libux; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + + class libu: public libx + { + public: + using libx::libx; + + public: + static const target_type static_type; + virtual const target_type& dynamic_type () const {return static_type;} + }; + // The lib{} target group. // class liba: public file @@ -149,10 +216,10 @@ namespace build2 const libs* s = nullptr; }; - class lib: public target, public lib_members + class lib: public libx, public lib_members { public: - using target::target; + using libx::libx; virtual group_view group_members (action_type) const override; diff --git a/build2/cc/common.cxx b/build2/cc/common.cxx index ec89e98..8dabb07 100644 --- a/build2/cc/common.cxx +++ b/build2/cc/common.cxx @@ -48,7 +48,7 @@ namespace build2 process_libraries ( action act, const scope& top_bs, - lorder top_lo, + linfo top_li, const dir_paths& top_sysd, const file& l, bool la, @@ -189,7 +189,7 @@ namespace build2 } const scope& bs (t == nullptr || cc ? top_bs : l.base_scope ()); - optional lo; // Calculate lazily. + optional li; // Calculate lazily. const dir_paths* sysd (nullptr); // Resolve lazily. // Find system search directories corresponding to this library, i.e., @@ -207,9 +207,11 @@ namespace build2 : var_pool[*t + ".sys_lib_dirs"]]); }; - auto find_lo = [top_lo, t, cc, &bs, &l, &lo] () + auto find_linfo = [top_li, t, cc, &bs, &l, &li] () { - lo = (t == nullptr || cc) ? top_lo : link_order (bs, link_type (l)); + li = (t == nullptr || cc) + ? top_li + : link_info (bs, link_type (l).type); }; // Only go into prerequisites (implementation) if instructed and we are @@ -223,13 +225,14 @@ namespace build2 bool a; const file* f; - if ((a = (f = p->is_a ()) != nullptr) - || (f = p->is_a ()) != nullptr) + if ((a = (f = p->is_a ())) || + (a = (f = p->is_a ())) || + ( f = p->is_a ())) { if (sysd == nullptr) find_sysd (); - if (!lo) find_lo (); + if (!li) find_linfo (); - process_libraries (act, bs, *lo, *sysd, + process_libraries (act, bs, *li, *sysd, *f, a, proc_impl, proc_lib, proc_opt, true); } @@ -266,8 +269,8 @@ namespace build2 auto proc_int = [&l, &proc_impl, &proc_lib, &proc_opt, &sysd, &usrd, - &find_sysd, &find_lo, &sys_simple, - &bs, act, &lo, this] (const lookup& lu) + &find_sysd, &find_linfo, &sys_simple, + &bs, act, &li, this] (const lookup& lu) { const vector* ns (cast_null> (lu)); if (ns == nullptr || ns->empty ()) @@ -290,9 +293,9 @@ namespace build2 // This is a potentially project-qualified target. // if (sysd == nullptr) find_sysd (); - if (!lo) find_lo (); + if (!li) find_linfo (); - const file& t (resolve_library (act, bs, n, *lo, *sysd, usrd)); + const file& t (resolve_library (act, bs, n, *li, *sysd, usrd)); if (proc_lib) { @@ -313,8 +316,8 @@ namespace build2 // Process it recursively. // - process_libraries (act, bs, *lo, *sysd, - t, t.is_a (), + process_libraries (act, bs, *li, *sysd, + t, t.is_a () || t.is_a (), proc_impl, proc_lib, proc_opt, true); } } @@ -394,7 +397,7 @@ namespace build2 resolve_library (action act, const scope& s, name n, - lorder lo, + linfo li, const dir_paths& sysd, optional& usrd) const { @@ -440,10 +443,10 @@ namespace build2 fail << "unable to find library " << pk; } - // If this is lib{}, pick appropriate member. + // If this is lib{}/libu{}, pick appropriate member. // - if (const lib* l = xt->is_a ()) - xt = &link_member (*l, act, lo); // Pick liba{} or libs{}. + if (const libx* l = xt->is_a ()) + xt = &link_member (*l, act, li); // Pick lib*{e,a,s}{}. return xt->as (); } @@ -488,6 +491,9 @@ namespace build2 // @@ This is hairy enough to warrant a separate implementation for // Windows. + + // Note: since we are searching for a (presumably) installed library, + // utility libraries do not apply. // bool l (p.is_a ()); const optional& ext (l ? nullopt : p.tk.ext); // Only liba/libs. diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx index abeadfc..6e1d8b9 100644 --- a/build2/cc/common.hxx +++ b/build2/cc/common.hxx @@ -194,7 +194,7 @@ namespace build2 process_libraries ( action, const scope&, - lorder, + linfo, const dir_paths&, const file&, bool, @@ -232,7 +232,7 @@ namespace build2 resolve_library (action, const scope&, name, - lorder, + linfo, const dir_paths&, optional&) const; diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx index 805ad82..8fa3296 100644 --- a/build2/cc/compile.cxx +++ b/build2/cc/compile.cxx @@ -256,7 +256,7 @@ namespace build2 cstrings& args, const target& t, action act, - lorder lo) const + linfo li) const { auto opt = [&args, this] ( const file& l, const string& t, bool com, bool exp) @@ -284,14 +284,16 @@ namespace build2 // if (const target* pt = p.load ()) { - bool a; + if (const libx* l = pt->is_a ()) + pt = &link_member (*l, act, li); - if (const lib* l = pt->is_a ()) - a = (pt = &link_member (*l, act, lo))->is_a (); - else if (!(a = pt->is_a ()) && !pt->is_a ()) + bool a; + if (!((a = pt->is_a ()) || + (a = pt->is_a ()) || + pt->is_a ())) continue; - process_libraries (act, bs, lo, sys_lib_dirs, + process_libraries (act, bs, li, sys_lib_dirs, pt->as (), a, nullptr, nullptr, optf); } @@ -303,7 +305,7 @@ namespace build2 sha256& cs, const target& t, action act, - lorder lo) const + linfo li) const { auto opt = [&cs, this] ( const file& l, const string& t, bool com, bool exp) @@ -326,14 +328,16 @@ namespace build2 { if (const target* pt = p.load ()) { - bool a; + if (const libx* l = pt->is_a ()) + pt = &link_member (*l, act, li); - if (const lib* l = pt->is_a ()) - a = (pt = &link_member (*l, act, lo))->is_a (); - else if (!(a = pt->is_a ()) && !pt->is_a ()) + bool a; + if (!((a = pt->is_a ()) || + (a = pt->is_a ()) || + pt->is_a ())) continue; - process_libraries (act, bs, lo, sys_lib_dirs, + process_libraries (act, bs, li, sys_lib_dirs, pt->as (), a, nullptr, nullptr, optf); } @@ -348,7 +352,7 @@ namespace build2 prefix_map& m, target& t, action act, - lorder lo) const + linfo li) const { auto opt = [&m, this] ( const file& l, const string& t, bool com, bool exp) @@ -371,14 +375,16 @@ namespace build2 { if (const target* pt = p.load ()) { - bool a; + if (const libx* l = pt->is_a ()) + pt = &link_member (*l, act, li); - if (const lib* l = pt->is_a ()) - a = (pt = &link_member (*l, act, lo))->is_a (); - else if (!(a = pt->is_a ()) && !pt->is_a ()) + bool a; + if (!((a = pt->is_a ()) || + (a = pt->is_a ()) || + pt->is_a ())) continue; - process_libraries (act, bs, lo, sys_lib_dirs, + process_libraries (act, bs, li, sys_lib_dirs, pt->as (), a, nullptr, nullptr, optf); } @@ -461,9 +467,9 @@ namespace build2 const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); - otype ct (compile_type (t, mod)); - lorder lo (link_order (bs, ct)); - compile_target_types tt (compile_types (ct)); + otype ot (compile_type (t, mod)); + linfo li (link_info (bs, ot)); // Link info for selecting libraries. + compile_target_types tt (compile_types (ot)); // Derive file name from target name. // @@ -473,7 +479,7 @@ namespace build2 if (tsys == "win32-msvc") { - switch (ct) + switch (ot) { case otype::e: e = "exe."; break; case otype::a: e = "lib."; break; @@ -483,7 +489,7 @@ namespace build2 } else if (tsys == "mingw32") { - switch (ct) + switch (ot) { case otype::e: e = "exe."; break; case otype::a: e = "a."; break; @@ -492,7 +498,7 @@ namespace build2 } else if (tsys == "darwin") { - switch (ct) + switch (ot) { case otype::e: e = ""; break; case otype::a: e = "a."; break; @@ -501,7 +507,7 @@ namespace build2 } else { - switch (ct) + switch (ot) { case otype::e: e = ""; break; case otype::a: e = "a."; break; @@ -573,7 +579,10 @@ namespace build2 // *.export.poptions, modules, etc. This is the "library // meta-information protocol". See also append_lib_options(). // - if (p.is_a () || p.is_a () || p.is_a ()) + if (p.is_a () || + p.is_a () || + p.is_a () || + p.is_a ()) { if (act.operation () == update_id) { @@ -592,8 +601,8 @@ namespace build2 pt = &p.search (t); - if (const lib* l = pt->is_a ()) - pt = &link_member (*l, act, lo); + if (const libx* l = pt->is_a ()) + pt = &link_member (*l, act, li); } else continue; @@ -633,11 +642,12 @@ namespace build2 // match in link::apply() it will be safe unless someone is building // an obj?{} target directory. // - if (build2::match (act, - *pt, - pt->is_a () || pt->is_a () - ? unmatch::safe - : unmatch::none)) + if (build2::match ( + act, + *pt, + pt->is_a () || pt->is_a () || pt->is_a () + ? unmatch::safe + : unmatch::none)) pt = nullptr; // Ignore in execute. } @@ -718,7 +728,7 @@ namespace build2 // Hash *.export.poptions from prerequisite libraries. // - hash_lib_options (bs, cs, t, act, lo); + hash_lib_options (bs, cs, t, act, li); // Extra system header dirs (last). // @@ -730,7 +740,7 @@ namespace build2 hash_options (cs, t, x_coptions); hash_options (cs, tstd); - if (ct == otype::s) + if (ot == otype::s) { // On Darwin, Win32 -fPIC is the default. // @@ -795,7 +805,7 @@ namespace build2 // pair psrc (auto_rmfile (), false); if (md.pp < preprocessed::includes) - psrc = extract_headers (act, t, lo, src, md, dd, u, mt); + psrc = extract_headers (act, t, li, src, md, dd, u, mt); // Next we "obtain" the translation unit information. What exactly // "obtain" entails is tricky: If things changed, then we re-parse the @@ -818,7 +828,7 @@ namespace build2 { if (u) { - auto p (parse_unit (act, t, lo, src, psrc.first, md)); + auto p (parse_unit (act, t, li, src, psrc.first, md)); if (cs != p.second) { @@ -875,7 +885,7 @@ namespace build2 // NOTE: assumes that no further targets will be added into // t.prerequisite_targets! // - extract_modules (act, t, lo, tt, src, md, move (tu.mod), dd, u); + extract_modules (act, t, li, tt, src, md, move (tu.mod), dd, u); } // If anything got updated, then we didn't rely on the cache. However, @@ -1073,7 +1083,7 @@ namespace build2 auto compile:: build_prefix_map (const scope& bs, target& t, - action act, lorder lo) const -> prefix_map + action act, linfo li) const -> prefix_map { prefix_map m; @@ -1084,7 +1094,7 @@ namespace build2 // Then process the include directories from prerequisite libraries. // - append_lib_prefixes (bs, m, t, act, lo); + append_lib_prefixes (bs, m, t, act, li); return m; } @@ -1273,7 +1283,7 @@ namespace build2 pair compile:: extract_headers (action act, file& t, - lorder lo, + linfo li, const file& src, const match_data& md, depdb& dd, @@ -1429,7 +1439,7 @@ namespace build2 // Return NULL if the dependency information goes to stdout and a // pointer to the temporary file path otherwise. // - auto init_args = [&t, act, lo, + auto init_args = [&t, act, li, &src, &md, &psrc, &sense_diag, &rs, &bs, pp, &env, &args, &args_gen, &args_i, &out, &drm, this] @@ -1477,7 +1487,7 @@ namespace build2 // Add *.export.poptions from prerequisite libraries. // - append_lib_options (bs, args, t, act, lo); + append_lib_options (bs, args, t, act, li); append_options (args, t, c_poptions); append_options (args, t, x_poptions); @@ -1715,7 +1725,7 @@ namespace build2 // extraction process should be restarted. // auto add = [&trace, &pm, - act, &t, lo, + act, &t, li, &dd, &updating, mt, &bs, this] (path f, bool cache) -> bool { @@ -1845,7 +1855,7 @@ namespace build2 // then we would have failed below. // if (pm.empty ()) - pm = build_prefix_map (bs, t, act, lo); + pm = build_prefix_map (bs, t, act, li); // First try the whole file. Then just the directory. // @@ -2340,7 +2350,7 @@ namespace build2 pair compile:: parse_unit (action act, file& t, - lorder lo, + linfo lo, const file& src, auto_rmfile& psrc, const match_data& md) const @@ -2592,7 +2602,7 @@ namespace build2 void compile:: extract_modules (action act, file& t, - lorder lo, + linfo li, const compile_target_types& tt, const file& src, match_data& md, @@ -2648,7 +2658,7 @@ namespace build2 sha256 cs; if (!mi.imports.empty ()) - md.mods = search_modules (act, t, lo, tt.bmi, src, mi.imports, cs); + md.mods = search_modules (act, t, li, tt.bmi, src, mi.imports, cs); if (dd.expect (cs.string ()) != nullptr) updating = true; @@ -2708,7 +2718,7 @@ namespace build2 module_positions compile:: search_modules (action act, file& t, - lorder lo, + linfo li, const target_type& mtt, const file& src, module_imports& imports, @@ -2959,9 +2969,9 @@ namespace build2 { const target* lt (nullptr); - if (const lib* l = pt->is_a ()) - lt = &link_member (*l, act, lo); - else if (pt->is_a () || pt->is_a ()) + if (const libx* l = pt->is_a ()) + lt = &link_member (*l, act, li); + else if (pt->is_a () || pt->is_a () || pt->is_a ()) lt = pt; // If this is a library, check its bmi{}s. @@ -3371,8 +3381,8 @@ namespace build2 const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); - otype ct (compile_type (t, mod)); - lorder lo (link_order (bs, ct)); + otype ot (compile_type (t, mod)); + linfo li (link_info (bs, ot)); environment env; cstrings args {cpath.recall_string ()}; @@ -3398,7 +3408,7 @@ namespace build2 // Add *.export.poptions from prerequisite libraries. // - append_lib_options (bs, args, t, act, lo); + append_lib_options (bs, args, t, act, li); // Extra system header dirs (last). // @@ -3521,7 +3531,7 @@ namespace build2 } else { - if (ct == otype::s) + if (ot == otype::s) { // On Darwin, Win32 -fPIC is the default. // diff --git a/build2/cc/compile.hxx b/build2/cc/compile.hxx index afd0cfc..ffe9fd7 100644 --- a/build2/cc/compile.hxx +++ b/build2/cc/compile.hxx @@ -62,13 +62,13 @@ namespace build2 append_lib_options (const scope&, cstrings&, const target&, - action, lorder) const; + action, linfo) const; void hash_lib_options (const scope&, sha256&, const target&, - action, lorder) const; + action, linfo) const; // Mapping of include prefixes (e.g., foo in ) for auto- // generated headers to directories where they will be generated. @@ -89,10 +89,10 @@ namespace build2 append_lib_prefixes (const scope&, prefix_map&, target&, - action, lorder) const; + action, linfo) const; prefix_map - build_prefix_map (const scope&, target&, action, lorder) const; + build_prefix_map (const scope&, target&, action, linfo) const; // Reverse-lookup target type from extension. // @@ -100,21 +100,21 @@ namespace build2 map_extension (const scope&, const string&, const string&) const; pair - extract_headers (action, file&, lorder, + extract_headers (action, file&, linfo, const file&, const match_data&, depdb&, bool&, timestamp) const; pair - parse_unit (action, file&, lorder, + parse_unit (action, file&, linfo, const file&, auto_rmfile&, const match_data&) const; void - extract_modules (action, file&, lorder, const compile_target_types&, + extract_modules (action, file&, linfo, const compile_target_types&, const file&, match_data&, module_info&&, depdb&, bool&) const; module_positions - search_modules (action, file&, lorder, const target_type&, + search_modules (action, file&, linfo, const target_type&, const file&, module_imports&, sha256&) const; void diff --git a/build2/cc/install.cxx b/build2/cc/install.cxx index 529a758..c35e931 100644 --- a/build2/cc/install.cxx +++ b/build2/cc/install.cxx @@ -48,7 +48,7 @@ namespace build2 // if (const lib* l = pt->is_a ()) pt = &link_member ( - *l, a, link_order (t.base_scope (), link_type (t))); + *l, a, link_info (t.base_scope (), link_type (t).type)); if (pt->is_a ()) // Can be liba{}. return pt->in (t.weak_scope ()) ? pt : nullptr; diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index b113adb..7b14621 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -56,15 +56,18 @@ namespace build2 // (i.e., a utility library). // - otype lt (link_type (t)); + ltype lt (link_type (t)); + otype ot (lt.type); - // If this is a library, link-up to our group (this is the lib{} target - // group protocol which means this can be done whether we match or not). + // If this is a library, link-up to our group (this is the target group + // protocol which means this can be done whether we match or not). // - if (lt == otype::a || lt == otype::s) + if (lt.library ()) { if (t.group == nullptr) - t.group = targets.find (t.dir, t.out, t.name); + t.group = targets.find ( + lt.utility ? libu::static_type : lib::static_type, + t.dir, t.out, t.name); } // Scan prerequisites and see if we can work with what we've got. Note @@ -90,28 +93,29 @@ namespace build2 } else if (p.is_a () || p.is_a ()) { - if (lt != otype::e) + if (ot != otype::e) fail << p.type ().name << "{} as prerequisite of " << t; seen_obj = seen_obj || true; } else if (p.is_a () || p.is_a ()) { - if (lt != otype::a) + if (ot != otype::a) fail << p.type ().name << "{} as prerequisite of " << t; seen_obj = seen_obj || true; } else if (p.is_a () || p.is_a ()) { - if (lt != otype::s) + if (ot != otype::s) fail << p.type ().name << "{} as prerequisite of " << t; seen_obj = seen_obj || true; } - else if (p.is_a () || - p.is_a () || - p.is_a ()) + else if (p.is_a () || + p.is_a () || + p.is_a () || + p.is_a ()) { seen_lib = seen_lib || true; } @@ -300,12 +304,13 @@ namespace build2 const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); - otype lt (link_type (t)); - lorder lo (link_order (bs, lt)); + ltype lt (link_type (t)); + otype ot (lt.type); + linfo li (link_info (bs, ot)); // Set the library type (C, C++, etc). // - if (lt != otype::e) + if (lt.library ()) t.vars.assign (c_type) = string (x); // Derive file name(s) and add ad hoc group members. @@ -325,57 +330,119 @@ namespace build2 const char* p (nullptr); // Prefix. const char* s (nullptr); // Suffix. - switch (lt) + if (lt.utility) { - case otype::e: + // These are all static libraries with names indicating the kind of + // object files they contain (similar to how we name object files + // themselves). We add the 'u' extension to avoid clashes with + // real libraries/import stubs. + // + // libue libhello.u.a hello.exe.u.lib + // libua libhello.a.u.a hello.lib.u.lib + // libus libhello.so.u.a hello.dll.u.lib hello.dylib.u.lib + // + // Note that we currently don't add bin.lib.{prefix,suffix} since + // these are not installed. + // + if (tsys == "win32-msvc") { - if (tclass == "windows") - e = "exe"; - else - e = ""; + switch (ot) + { + case otype::e: e = "exe.u.lib"; break; + case otype::a: e = "lib.u.lib"; break; + case otype::s: e = "dll.u.lib"; break; + } + } + else + { + p = "lib"; - if (auto l = t["bin.exe.prefix"]) p = cast (l).c_str (); - if (auto l = t["bin.exe.suffix"]) s = cast (l).c_str (); + if (tsys == "mingw32") + { + switch (ot) + { + case otype::e: e = "exe.u.a"; break; + case otype::a: e = "a.u.a"; break; + case otype::s: e = "dll.u.a"; break; + } - t.derive_path (e, p, s); - break; + } + else if (tsys == "darwin") + { + switch (ot) + { + case otype::e: e = "u.a"; break; + case otype::a: e = "a.u.a"; break; + case otype::s: e = "dylib.u.a"; break; + } + } + else + { + switch (ot) + { + case otype::e: e = "u.a"; break; + case otype::a: e = "a.u.a"; break; + case otype::s: e = "so.u.a"; break; + } + } } - case otype::a: + + t.derive_path (e, p, s); + } + else + { + switch (ot) { - if (cid == compiler_id::msvc) - e = "lib"; - else + case otype::e: { - p = "lib"; - e = "a"; + if (tclass == "windows") + e = "exe"; + else + e = ""; + + if (auto l = t["bin.exe.prefix"]) p = cast (l).c_str (); + if (auto l = t["bin.exe.suffix"]) s = cast (l).c_str (); + + t.derive_path (e, p, s); + break; } + case otype::a: + { + if (cid == compiler_id::msvc) + e = "lib"; + else + { + p = "lib"; + e = "a"; + } - if (auto l = t["bin.lib.prefix"]) p = cast (l).c_str (); - if (auto l = t["bin.lib.suffix"]) s = cast (l).c_str (); + if (auto l = t["bin.lib.prefix"]) p = cast (l).c_str (); + if (auto l = t["bin.lib.suffix"]) s = cast (l).c_str (); - t.derive_path (e, p, s); - break; - } - case otype::s: - { - // On Windows libs{} is an ad hoc group. The libs{} itself is the - // DLL and we add libi{} import library as its member. - // - if (tclass == "windows") - libi = add_adhoc (t, "libi"); + t.derive_path (e, p, s); + break; + } + case otype::s: + { + // On Windows libs{} is an ad hoc group. The libs{} itself is + // the DLL and we add libi{} import library as its member. + // + if (tclass == "windows") + libi = add_adhoc (t, "libi"); - t.data (derive_libs_paths (t)); // Cache in target. + t.data (derive_libs_paths (t)); // Cache in target. - if (libi) - match_recipe (libi, group_recipe); // Set recipe and unlock. + if (libi) + match_recipe (libi, group_recipe); // Set recipe and unlock. - break; + break; + } } } // PDB // - if (lt != otype::a && + if (!lt.static_library () && cid == compiler_id::msvc && (find_option ("/DEBUG", t, c_loptions, true) || find_option ("/DEBUG", t, x_loptions, true))) @@ -419,7 +486,7 @@ namespace build2 // or a subdirectory of our project root. // optional usr_lib_dirs; // Extract lazily. - compile_target_types tt (compile_types (lt)); + compile_target_types tt (compile_types (ot)); auto skip = [&act, &rs] (const target*& pt) { @@ -505,7 +572,10 @@ namespace build2 m = mod ? 2 : 1; } - else if (p.is_a () || p.is_a () || p.is_a ()) + else if (p.is_a () || + p.is_a () || + p.is_a () || + p.is_a ()) { // Handle imported libraries. // @@ -524,11 +594,11 @@ namespace build2 if (skip (pt)) continue; - // If this is the lib{} target group, then pick the appropriate + // If this is the lib{}/libu{} group, then pick the appropriate // member. // - if (const lib* l = pt->is_a ()) - pt = &link_member (*l, act, lo); + if (const libx* l = pt->is_a ()) + pt = &link_member (*l, act, li); } else { @@ -622,8 +692,9 @@ namespace build2 { const target* pt (t.prerequisite_targets[j++]); - if (p.is_a () || p.is_a () || p.is_a () || - p.is_a () || p.is_a (tt.bmi)) + if (p.is_a () || + p.is_a () || p.is_a () || p.is_a () || + p.is_a () || p.is_a (tt.bmi)) { ps.emplace_back (p.as_prerequisite ()); } @@ -681,12 +752,12 @@ namespace build2 // Ignore some known target types (fsdir, headers, libraries, // modules). // - if (p1.is_a () || - p1.is_a () || - p1.is_a () || p1.is_a () || - p1.is_a () || - p1.is_a () || p1.is_a () || p1.is_a () || - (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) || + if (p1.is_a () || + p1.is_a () || + p1.is_a () || p1.is_a () || p1.is_a () || + p1.is_a () || + p1.is_a () || p1.is_a () || p1.is_a () || + (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) || (p.is_a () && p1.is_a ())) continue; @@ -805,7 +876,7 @@ namespace build2 void link:: append_libraries (strings& args, const file& l, bool la, - const scope& bs, action act, lorder lo) const + const scope& bs, action act, linfo li) const { // Note: lack of the "small function object" optimization will really // kill us here since we are called in a loop. @@ -850,13 +921,13 @@ namespace build2 }; process_libraries ( - act, bs, lo, sys_lib_dirs, l, la, imp, lib, opt, true); + act, bs, li, sys_lib_dirs, l, la, imp, lib, opt, true); } void link:: hash_libraries (sha256& cs, const file& l, bool la, - const scope& bs, action act, lorder lo) const + const scope& bs, action act, linfo li) const { bool win (tclass == "windows"); @@ -896,7 +967,7 @@ namespace build2 }; process_libraries ( - act, bs, lo, sys_lib_dirs, l, la, imp, lib, opt, true); + act, bs, li, sys_lib_dirs, l, la, imp, lib, opt, true); } void link:: @@ -904,7 +975,7 @@ namespace build2 const target& t, const scope& bs, action act, - lorder lo, + linfo li, bool for_install) const { // Use -rpath-link on targets that support it (Linux, *BSD). Note @@ -945,7 +1016,7 @@ namespace build2 auto lib = [&d, this] (const file* l, const string& f, bool sys) { // We don't rpath system libraries. Why, you may ask? There are many - // good reasons and I have them written on an napkin somewhere... + // good reasons and I have them written on a napkin somewhere... // if (sys) return; @@ -1001,13 +1072,14 @@ namespace build2 for (const target* pt: t.prerequisite_targets) { + bool a; const file* f; - const liba* a; - if ((f = a = pt->is_a ()) || - (f = pt->is_a ())) + if ((a = (f = pt->is_a ())) || + (a = (f = pt->is_a ())) || + ( f = pt->is_a ())) { - if (!for_install && a == nullptr) + if (!for_install && !a) { // Top-level sharen library dependency. It is either matched or // imported so should be a cc library. @@ -1017,8 +1089,8 @@ namespace build2 "-Wl,-rpath," + f->path ().directory ().string ()); } - process_libraries (act, bs, lo, sys_lib_dirs, - *f, a != nullptr, + process_libraries (act, bs, li, sys_lib_dirs, + *f, a, impf, libf, nullptr); } } @@ -1048,8 +1120,9 @@ namespace build2 const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); - otype lt (link_type (t)); - lorder lo (link_order (bs, lt)); + ltype lt (link_type (t)); + otype ot (lt.type); + linfo li (link_info (bs, ot)); // Update prerequisites. We determine if any relevant ones render us // out-of-date manually below. @@ -1063,14 +1136,14 @@ namespace build2 path manifest; // Manifest itself (msvc) or compiled object file. timestamp rpath_timestamp (timestamp_nonexistent); // DLLs timestamp. - if (lt == otype::e && tclass == "windows") + if (lt.executable () && tclass == "windows") { // First determine if we need to add our rpath emulating assembly. The // assembly itself is generated later, after updating the target. Omit // it if we are updating for install. // if (!for_install) - rpath_timestamp = windows_rpath_timestamp (t, bs, act, lo); + rpath_timestamp = windows_rpath_timestamp (t, bs, act, li); pair p ( windows_manifest (t, @@ -1182,7 +1255,7 @@ namespace build2 // Then the linker checksum (ar/ranlib or the compiler). // - if (lt == otype::a) + if (lt.static_library ()) { ranlib = rs["bin.ranlib.path"]; @@ -1232,7 +1305,7 @@ namespace build2 string soname1, soname2; strings sargs; - if (lt == otype::a) + if (lt.static_library ()) { if (cid == compiler_id::msvc) ; else @@ -1277,7 +1350,7 @@ namespace build2 { // Set soname. // - if (lt == otype::s) + if (lt.shared_library ()) { const libs_paths& paths (t.data ()); const string& leaf (paths.effect_soname ().leaf ().string ()); @@ -1315,7 +1388,7 @@ namespace build2 // rpath of the imported libraries (i.e., we assume they are also // installed). But we add -rpath-link for some platforms. // - rpath_libraries (sargs, t, bs, act, lo, for_install); + rpath_libraries (sargs, t, bs, act, li, for_install); if (auto l = t["bin.rpath"]) for (const dir_path& p: cast (l)) @@ -1350,10 +1423,6 @@ namespace build2 for (const target* pt: t.prerequisite_targets) { - const file* f; - const liba* a (nullptr); - const libs* s (nullptr); - // If this is bmi*{}, then obj*{} is its ad hoc member. // if (modules) @@ -1362,18 +1431,22 @@ namespace build2 pt = pt->member; } + const file* f; + bool a (false), s (false); + if ((f = pt->is_a ()) || (f = pt->is_a ()) || (f = pt->is_a ()) || - (lt != otype::a && - ((f = a = pt->is_a ()) || - (f = s = pt->is_a ())))) + (!lt.static_library () && // @@ UTL: TODO libua to liba link. + ((a = (f = pt->is_a ())) || + (a = (f = pt->is_a ())) || + (s = (f = pt->is_a ()))))) { // Link all the dependent interface libraries (shared) or interface // and implementation (static), recursively. // - if (a != nullptr || s != nullptr) - hash_libraries (cs, *f, a != nullptr, bs, act, lo); + if (a || s) + hash_libraries (cs, *f, a, bs, act, li); else cs.append (f->path ().string ()); } @@ -1393,7 +1466,7 @@ namespace build2 // Treat them as inputs, not options. // - if (lt != otype::a) + if (!lt.static_library ()) { hash_options (cs, t, c_libs); hash_options (cs, t, x_libs); @@ -1429,13 +1502,13 @@ namespace build2 path relt (relative (tp)); const process_path* ld (nullptr); - switch (lt) + if (lt.static_library ()) { - case otype::a: - { - ld = &cast (rs["bin.ar.path"]); + ld = &cast (rs["bin.ar.path"]); - if (cid == compiler_id::msvc) + switch (cid) + { + case compiler_id::msvc: { // lib.exe has /LIBPATH but it's not clear/documented what it's // used for. Perhaps for link-time code generation (/LTCG)? If @@ -1449,25 +1522,30 @@ namespace build2 out = "/OUT:" + relt.string (); args.push_back (out.c_str ()); + break; } - else + default: + { args.push_back (relt.string ().c_str ()); - - break; + break; + } } - // The options are usually similar enough to handle them together. + } + else + { + // The options are usually similar enough to handle executables + // and shared libraries together. // - case otype::e: - case otype::s: + switch (cid) { - if (cid == compiler_id::msvc) + case compiler_id::msvc: { // Using link.exe directly. // ld = &cast (rs["bin.ld.path"]); args.push_back ("/NOLOGO"); - if (lt == otype::s) + if (ot == otype::s) args.push_back ("/DLL"); // Add /MACHINE. @@ -1519,7 +1597,7 @@ namespace build2 args.push_back (out3.c_str ()); } - if (lt == otype::s) + if (ot == otype::s) { // On Windows libs{} is the DLL and its first ad hoc group // member is the import library. @@ -1539,7 +1617,7 @@ namespace build2 if (find_option ("/DEBUG", args, true)) { auto& pdb ( - (lt == otype::e ? t.member : t.member->member)->as ()); + (ot == otype::e ? t.member : t.member->member)->as ()); out1 = "/PDB:" + relative (pdb.path ()).string (); args.push_back (out1.c_str ()); } @@ -1553,15 +1631,16 @@ namespace build2 // out = "/OUT:" + relt.string (); args.push_back (out.c_str ()); + break; } - else + default: { ld = &cpath; // Add the option that triggers building a shared library and take // care of any extras (e.g., import library). // - if (lt == otype::s) + if (ot == otype::s) { if (tclass == "macos") args.push_back ("-dynamiclib"); @@ -1581,9 +1660,8 @@ namespace build2 args.push_back ("-o"); args.push_back (relt.string ().c_str ()); + break; } - - break; } } @@ -1593,28 +1671,28 @@ namespace build2 // for (const target* pt: t.prerequisite_targets) { - const file* f; - const liba* a (nullptr); - const libs* s (nullptr); - if (modules) { if (pt->is_a () || pt->is_a () || pt->is_a ()) pt = pt->member; } + const file* f; + bool a (false), s (false); + if ((f = pt->is_a ()) || (f = pt->is_a ()) || (f = pt->is_a ()) || - (lt != otype::a && - ((f = a = pt->is_a ()) || - (f = s = pt->is_a ())))) + (!lt.static_library () && // @@ UTL: TODO libua to liba link. + ((a = (f = pt->is_a ())) || + (a = (f = pt->is_a ())) || + (s = (f = pt->is_a ()))))) { // Link all the dependent interface libraries (shared) or interface // and implementation (static), recursively. // - if (a != nullptr || s != nullptr) - append_libraries (sargs, *f, a != nullptr, bs, act, lo); + if (a || s) + append_libraries (sargs, *f, a, bs, act, li); else sargs.push_back (relative (f->path ()).string ()); // string()&& } @@ -1631,7 +1709,7 @@ namespace build2 for (const string& a: sargs) args.push_back (a.c_str ()); - if (lt != otype::a) + if (!lt.static_library ()) { append_options (args, t, c_libs); append_options (args, t, x_libs); @@ -1641,7 +1719,7 @@ namespace build2 // Cleanup old (versioned) libraries. // - if (lt == otype::s) + if (lt.shared_library ()) { const libs_paths& paths (t.data ()); const path& p (paths.clean); @@ -1697,7 +1775,7 @@ namespace build2 // something like this) we are going to redirect stdout to stderr. For // sane compilers this should be harmless. // - bool filter (cid == compiler_id::msvc && lt != otype::a); + bool filter (cid == compiler_id::msvc && !lt.static_library ()); process pr (*ld, args.data (), 0, (filter ? -1 : 2)); @@ -1708,7 +1786,7 @@ namespace build2 ifdstream is ( move (pr.in_ofd), fdstream_mode::text, ifdstream::badbit); - msvc_filter_link (is, t, lt); + msvc_filter_link (is, t, ot); // If anything remains in the stream, send it all to stderr. Note // that the eof check is important: if the stream is at eof, this @@ -1780,13 +1858,13 @@ namespace build2 // For Windows generate rpath-emulating assembly (unless updaing for // install). // - if (lt == otype::e && !for_install) - windows_rpath_assembly (t, bs, act, lo, + if (lt.executable () && !for_install) + windows_rpath_assembly (t, bs, act, li, cast (rs[x_target_cpu]), rpath_timestamp, scratch); } - else if (lt == otype::s) + else if (lt.shared_library ()) { // For shared libraries we may need to create a bunch of symlinks. // @@ -1835,56 +1913,49 @@ namespace build2 perform_clean (action act, const target& xt) const { const file& t (xt.as ()); + ltype lt (link_type (t)); - switch (link_type (t)) + if (lt.executable ()) { - case otype::a: - break; // Default. - case otype::e: - { - if (tclass == "windows") - { - if (tsys == "mingw32") - return clean_extra ( - act, t, {".d", ".dlls/", ".manifest.o", ".manifest"}); - else - // Assuming it's VC or alike. Clean up .ilk in case the user - // enabled incremental linking (note that .ilk replaces .exe). - // - return clean_extra ( - act, t, {".d", ".dlls/", ".manifest", "-.ilk"}); - } - - break; - } - case otype::s: + if (tclass == "windows") { - if (tclass == "windows") - { - // Assuming it's VC or alike. Clean up .exp and .ilk. - // - // Note that .exp is based on the .lib, not .dll name. And with - // versioning their bases may not be the same. - // - if (tsys != "mingw32") - return clean_extra (act, t, {{".d", "-.ilk"}, {"-.exp"}}); - } + if (tsys == "mingw32") + return clean_extra ( + act, t, {".d", ".dlls/", ".manifest.o", ".manifest"}); else - { - // Here we can have a bunch of symlinks that we need to remove. If - // the paths are empty, then they will be ignored. + // Assuming it's VC or alike. Clean up .ilk in case the user + // enabled incremental linking (note that .ilk replaces .exe). // - const libs_paths& paths (t.data ()); - - return clean_extra (act, t, {".d", - paths.link.string ().c_str (), - paths.soname.string ().c_str (), - paths.interm.string ().c_str ()}); - } + return clean_extra ( + act, t, {".d", ".dlls/", ".manifest", "-.ilk"}); + } + } + else if (lt.shared_library ()) + { + if (tclass == "windows") + { + // Assuming it's VC or alike. Clean up .exp and .ilk. + // + // Note that .exp is based on the .lib, not .dll name. And with + // versioning their bases may not be the same. + // + if (tsys != "mingw32") + return clean_extra (act, t, {{".d", "-.ilk"}, {"-.exp"}}); + } + else + { + // Here we can have a bunch of symlinks that we need to remove. If + // the paths are empty, then they will be ignored. + // + const libs_paths& paths (t.data ()); - break; + return clean_extra (act, t, {".d", + paths.link.string ().c_str (), + paths.soname.string ().c_str (), + paths.interm.string ().c_str ()}); } } + // For static library it's just the defaults. return clean_extra (act, t, {".d"}); } diff --git a/build2/cc/link.hxx b/build2/cc/link.hxx index 4dc722a..0256774 100644 --- a/build2/cc/link.hxx +++ b/build2/cc/link.hxx @@ -76,17 +76,17 @@ namespace build2 void append_libraries (strings&, const file&, bool, - const scope&, action, lorder) const; + const scope&, action, linfo) const; void hash_libraries (sha256&, const file&, bool, - const scope&, action, lorder) const; + const scope&, action, linfo) const; void rpath_libraries (strings&, const target&, - const scope&, action, lorder, + const scope&, action, linfo, bool) const; // Windows rpath emulation (windows-rpath.cxx). @@ -105,13 +105,13 @@ namespace build2 timestamp windows_rpath_timestamp (const file&, const scope&, - action, lorder) const; + action, linfo) const; windows_dlls - windows_rpath_dlls (const file&, const scope&, action, lorder) const; + windows_rpath_dlls (const file&, const scope&, action, linfo) const; void - windows_rpath_assembly (const file&, const scope&, action, lorder, + windows_rpath_assembly (const file&, const scope&, action, linfo, const string&, timestamp, bool) const; diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx index 3755817..2b19e70 100644 --- a/build2/cc/module.cxx +++ b/build2/cc/module.cxx @@ -401,6 +401,18 @@ namespace build2 r.insert (configure_update_id, x_compile, cr); } + r.insert (perform_update_id, x_link, lr); + r.insert (perform_clean_id, x_link, lr); + r.insert (configure_update_id, x_link, lr); + + r.insert (perform_update_id, x_link, lr); + r.insert (perform_clean_id, x_link, lr); + r.insert (configure_update_id, x_link, lr); + + r.insert (perform_update_id, x_link, lr); + r.insert (perform_clean_id, x_link, lr); + r.insert (configure_update_id, x_link, lr); + r.insert (perform_update_id, x_link, lr); r.insert (perform_clean_id, x_link, lr); r.insert (configure_update_id, x_link, lr); @@ -413,6 +425,8 @@ namespace build2 r.insert (perform_clean_id, x_link, lr); r.insert (configure_update_id, x_link, lr); + // Note that libu*{} are not installable. + // if (install_loaded) { const install& ir (*this); diff --git a/build2/cc/types.hxx b/build2/cc/types.hxx index 3170942..417370f 100644 --- a/build2/cc/types.hxx +++ b/build2/cc/types.hxx @@ -61,6 +61,17 @@ namespace build2 // enum class otype {e, a, s}; + struct ltype + { + otype type; + bool utility; // True for utility libraries. + + bool executable () const {return type == otype::e || !utility;} + bool library () const {return type != otype::e || utility;} + bool static_library () const {return type == otype::a || utility;} + bool shared_library () const {return type == otype::s && !utility;} + }; + // Compile target types. // struct compile_target_types @@ -72,6 +83,14 @@ namespace build2 // Library link order. // enum class lorder {a, s, a_s, s_a}; + + // Link information: output type and link order. + // + struct linfo + { + otype type; + lorder order; + }; } } diff --git a/build2/cc/utility.cxx b/build2/cc/utility.cxx index 7a03b54..fa5061e 100644 --- a/build2/cc/utility.cxx +++ b/build2/cc/utility.cxx @@ -39,34 +39,50 @@ namespace build2 } const target& - link_member (const bin::lib& l, action a, lorder lo) + link_member (const bin::libx& x, action a, linfo li) { - // Make sure group members are resolved. - // - group_view gv (resolve_group_members (a, l)); - assert (gv.members != nullptr); - - bool ls (true); - switch (lo) + if (const libu* u = x.is_a ()) { - case lorder::a: - case lorder::a_s: - ls = false; // Fall through. - case lorder::s: - case lorder::s_a: + otype ot (li.type); + return search (*u, + ot == otype::e ? libue::static_type : + ot == otype::a ? libua::static_type : + libus::static_type, + u->dir, u->out, u->name); + } + else + { + const lib& l (x.as ()); + + // Make sure group members are resolved. + // + group_view gv (resolve_group_members (a, l)); + assert (gv.members != nullptr); + + lorder lo (li.order); + + bool ls (true); + switch (lo) { - if (ls ? l.s == nullptr : l.a == nullptr) + case lorder::a: + case lorder::a_s: + ls = false; // Fall through. + case lorder::s: + case lorder::s_a: { - if (lo == lorder::a_s || lo == lorder::s_a) - ls = !ls; - else - fail << (ls ? "shared" : "static") << " variant of " << l - << " is not available"; + if (ls ? l.s == nullptr : l.a == nullptr) + { + if (lo == lorder::a_s || lo == lorder::s_a) + ls = !ls; + else + fail << (ls ? "shared" : "static") << " variant of " << l + << " is not available"; + } } } - } - return *(ls ? static_cast (l.s) : l.a); + return *(ls ? static_cast (l.s) : l.a); + } } } } diff --git a/build2/cc/utility.hxx b/build2/cc/utility.hxx index 895d9c5..2f6834f 100644 --- a/build2/cc/utility.hxx +++ b/build2/cc/utility.hxx @@ -29,7 +29,7 @@ namespace build2 // Link output type. // - otype + ltype link_type (const target&); // Library link order. @@ -44,10 +44,17 @@ namespace build2 lorder link_order (const scope& base, otype); - // Given the link order return the library member (liba or libs) to link. + inline linfo + link_info (const scope& base, otype ot) + { + return linfo {ot, link_order (base, ot)}; + } + + // Given the link order return the library member to link. That is, liba{} + // or libs{} for lib{} and libue{}, libua{} or libus{} for libu{}. // const target& - link_member (const bin::lib&, action, lorder); + link_member (const bin::libx&, action, linfo); } } diff --git a/build2/cc/utility.ixx b/build2/cc/utility.ixx index d372dac..d13a0ff 100644 --- a/build2/cc/utility.ixx +++ b/build2/cc/utility.ixx @@ -17,15 +17,19 @@ namespace build2 otype::s; } - inline otype + inline ltype link_type (const target& t) { using namespace bin; - return - t.is_a () ? otype::e : - t.is_a () ? otype::a : - otype::s; + bool u (false); + otype o ( + t.is_a () || (u = t.is_a ()) ? otype::e : + t.is_a () || (u = t.is_a ()) ? otype::a : + t.is_a () || (u = t.is_a ()) ? otype::s : + static_cast (0xFF)); + + return ltype {o, u}; } inline compile_target_types diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx index 450a0f5..1fc195a 100644 --- a/build2/cc/windows-rpath.cxx +++ b/build2/cc/windows-rpath.cxx @@ -49,7 +49,7 @@ namespace build2 windows_rpath_timestamp (const file& t, const scope& bs, action act, - lorder lo) const + linfo li) const { timestamp r (timestamp_nonexistent); @@ -109,7 +109,7 @@ namespace build2 if ((f = a = pt->is_a ()) || (f = pt->is_a ())) - process_libraries (act, bs, lo, sys_lib_dirs, + process_libraries (act, bs, li, sys_lib_dirs, *f, a != nullptr, imp, lib, nullptr, true); } @@ -124,7 +124,7 @@ namespace build2 windows_rpath_dlls (const file& t, const scope& bs, action act, - lorder lo) const -> windows_dlls + linfo li) const -> windows_dlls { windows_dlls r; @@ -191,7 +191,7 @@ namespace build2 if ((f = a = pt->is_a ()) || (f = pt->is_a ())) - process_libraries (act, bs, lo, sys_lib_dirs, + process_libraries (act, bs, li, sys_lib_dirs, *f, a != nullptr, imp, lib, nullptr, true); } @@ -214,7 +214,7 @@ namespace build2 windows_rpath_assembly (const file& t, const scope& bs, action act, - lorder lo, + linfo li, const string& tcpu, timestamp ts, bool scratch) const @@ -251,7 +251,7 @@ namespace build2 windows_dlls dlls; if (!empty) - dlls = windows_rpath_dlls (t, bs, act, lo); + dlls = windows_rpath_dlls (t, bs, act, li); // Clean the assembly directory and make sure it exists. Maybe it would // have been faster to overwrite the existing manifest rather than diff --git a/tests/cc/libu/buildfile b/tests/cc/libu/buildfile new file mode 100644 index 0000000..104a645 --- /dev/null +++ b/tests/cc/libu/buildfile @@ -0,0 +1,8 @@ +# file : tests/cc/libu/buildfile +# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +# Test utility library support. +# + +./: test{testscript} $b diff --git a/tests/cc/libu/testscript b/tests/cc/libu/testscript new file mode 100644 index 0000000..454a443 --- /dev/null +++ b/tests/cc/libu/testscript @@ -0,0 +1,54 @@ +# file : tests/cc/libu/testscript +# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +crosstest = false +test.arguments = config.cxx="$recall($cxx.path)" + +.include ../../common.test + ++cat <=build/root.build +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +cxx{*}: extension = cxx +EOI + +# Common source files that are symlinked in the test directories if used. +# ++cat <=foo.hxx + #ifndef LIBFOO_EXPORT + # define LIBFOO_EXPORT + #endif + + LIBFOO_EXPORT void f (); + EOI + ++cat <=foo.cxx + void f () {} + EOI + ++cat <=driver.cxx + #include + int main () {f ();} + EOI + +: members +: +: Test building individual libuX{} members. +: +ln -s ../foo.hxx ../foo.cxx ../driver.cxx ./; +$* update clean <