diff options
Diffstat (limited to 'libbuild2/cc/common.cxx')
-rw-r--r-- | libbuild2/cc/common.cxx | 1464 |
1 files changed, 1093 insertions, 371 deletions
diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx index 9cac1b0..2a8bc50 100644 --- a/libbuild2/cc/common.cxx +++ b/libbuild2/cc/common.cxx @@ -24,7 +24,7 @@ namespace build2 // Recursively process prerequisite libraries of the specified library. If // proc_impl returns false, then only process interface (*.export.libs), // otherwise -- interface and implementation (prerequisite and from - // *.libs, unless overriden with *.export.imp_libs). + // *.libs, unless overriden with *.export.impl_libs). // // Note that here we assume that an interface library is also always an // implementation (since we don't use *.export.libs for static linking). @@ -32,10 +32,6 @@ namespace build2 // *.export.libs is up-to-date (which will happen automatically if it is // listed as a prerequisite of this library). // - // Storing a reference to library path in proc_lib is legal (it comes - // either from the target's path or from one of the *.libs variables - // neither of which should change on this run). - // // Note that the order of processing is: // // 1. options (x.* then cc.* to be consistent with poptions/loptions) @@ -43,289 +39,574 @@ namespace build2 // 3. dependency libs (prerequisite_targets, left to right, depth-first) // 4. dependency libs (*.libs variables). // + // If proc_opt_group is true, then pass to proc_opt the group rather than + // the member if a member was picked (according to linfo) form a group. + // This is useful when we only want to see the common options set on the + // group. + // + // If either proc_opt or proc_lib return false, then any further + // processing of this library or its dependencies is skipped. This can be + // used to "prune" the graph traversal in case of duplicates. Note that + // proc_opt is called twice for each library so carefully consider from + // which to return false. + // // The first argument to proc_lib is a pointer to the last element of an // array that contains the current library dependency chain all the way to // the library passed to process_libraries(). The first element of this - // array is NULL. + // array is NULL. If this argument is NULL, then this is a library without + // a target (e.g., -lm, -pthread, etc) and its name is in the second + // argument (which could be resolved to an absolute path or passed as an + // -l<name>/-pthread option). Otherwise, (the first argument is not NULL), + // the second argument contains the target path (which can be empty in + // case of the unknown DLL path). + // + // Initially, the second argument (library name) was a string (e.g., -lm) + // but there are cases where the library is identified with multiple + // options, such as -framework CoreServices (there are also cases like + // -Wl,--whole-archive -lfoo -lbar -Wl,--no-whole-archive). So now it is a + // vector_view that contains a fragment of options (from one of the *.libs + // variables) that corresponds to the library (or several libraries, as in + // the --whole-archive example above). + // + // Storing a reference to elements of library name in proc_lib is legal + // (they come either from the target's path or from one of the *.libs + // variables neither of which should change on this run). + // + // If proc_impl always returns false (that is, we are only interested in + // interfaces), then top_li can be absent. This makes process_libraries() + // not to pick the liba/libs{} member for installed libraries instead + // passing the lib{} group itself. This can be used to match the semantics + // of file_rule which, when matching prerequisites, does not pick the + // liba/libs{} member (naturally) but just matches the lib{} group. Note + // that currently this truly only works for installed lib{} since non- + // installed ones don't have cc.type set. See proc_opt_group for an + // alternative way to (potentially) achieve the desired semantics. + // + // Note that if top_li is present, then the target passed to proc_impl, + // proc_lib, and proc_opt (unless proc_opt_group is true) is always a + // file. + // + // The dedup argument is part of the interface dependency deduplication + // functionality, similar to $x.deduplicate_export_libs(). Note, however, + // that here we do it "properly" (i.e., using group members, etc). // void common:: process_libraries ( action a, const scope& top_bs, - linfo top_li, + optional<linfo> top_li, const dir_paths& top_sysd, - const file& l, + const mtime_target& l, // liba/libs{}, libux{}, or lib{} bool la, lflags lf, - const function<bool (const file&, - bool la)>& proc_impl, // Implementation? - const function<void (const file* const*, // Can be NULL. - const string& path, // Library path. - lflags, // Link flags. - bool sys)>& proc_lib, // True if system library. - const function<void (const file&, - const string& type, // cc.type - bool com, // cc. or x. - bool exp)>& proc_opt, // *.export. - bool self /*= false*/, // Call proc_lib on l? - small_vector<const file*, 16>* chain) const + const function<bool (const target&, + bool la)>& proc_impl, // Implementation? + const function<bool (const target* const*, // Can be NULL. + const small_vector<reference_wrapper< + const string>, 2>&, // Library "name". + lflags, // Link flags. + const string* type, // whole cc.type + bool sys)>& proc_lib, // System library? + const function<bool (const target&, + const string& lang, // lang from cc.type + bool com, // cc. or x. + bool exp)>& proc_opt, // *.export. + bool self, // Call proc_lib on l? + bool proc_opt_group, // Call proc_opt on group instead of member? + library_cache* cache) const { - small_vector<const file*, 16> chain_storage; - if (chain == nullptr) - { - chain = &chain_storage; - chain->push_back (nullptr); - } + library_cache cache_storage; + if (cache == nullptr) + cache = &cache_storage; + + small_vector<const target*, 32> chain; + + if (proc_lib) + chain.push_back (nullptr); + + process_libraries_impl (a, top_bs, top_li, top_sysd, + nullptr, l, la, lf, + proc_impl, proc_lib, proc_opt, + self, proc_opt_group, + cache, &chain, nullptr); + } - // See what type of library this is (C, C++, etc). Use it do decide - // which x.libs variable name to use. If it's unknown, then we only - // look into prerequisites. Note: lookup starting from rule-specific - // variables (target should already be matched). + void common:: + process_libraries_impl ( + action a, + const scope& top_bs, + optional<linfo> top_li, + const dir_paths& top_sysd, + const target* lg, + const mtime_target& l, + bool la, + lflags lf, + const function<bool (const target&, + bool la)>& proc_impl, + const function<bool (const target* const*, + const small_vector<reference_wrapper< + const string>, 2>&, + lflags, + const string* type, + bool sys)>& proc_lib, + const function<bool (const target&, + const string& lang, + bool com, + bool exp)>& proc_opt, + bool self, + bool proc_opt_group, + library_cache* cache, + small_vector<const target*, 32>* chain, + small_vector<const target*, 32>* dedup) const + { + // Add the library to the chain. // - const string* t (cast_null<string> (l.state[a][c_type])); + if (self && proc_lib) + { + if (find (chain->begin (), chain->end (), &l) != chain->end ()) + fail << "dependency cycle detected involving library " << l; - bool impl (proc_impl && proc_impl (l, la)); - bool cc (false), same (false); + chain->push_back (&l); + } + // We only lookup public variables so go straight for the public + // variable pool. + // auto& vp (top_bs.ctx.var_pool); - lookup c_e_libs; - lookup x_e_libs; - if (t != nullptr) + do // Breakout loop. { - cc = (*t == "cc"); - same = (!cc && *t == x); - - // Note that we used to treat *.export.libs set on the liba/libs{} - // members as *.libs overrides rather than as member-specific - // interface dependencies. This difference in semantics proved to be - // surprising so now we have separate *.export.imp_libs for that. - // Note that in this case options come from *.export.* variables. + // See what type of library this is (C, C++, etc). Use it do decide + // which x.libs variable name to use. If it's unknown, then we only + // look into prerequisites. Note: lookup starting from rule-specific + // variables (target should already be matched). Note also that for + // performance we use lookup_original() directly and only look in the + // target (so no target type/pattern-specific). + // + const string* pt ( + cast_null<string> ( + l.state[a].lookup_original (c_type, true /* target_only */).first)); + + // cc.type value format is <lang>[,...]. // - // Note also that we only check for *.*libs. If one doesn't have any - // libraries but needs to set, say, *.loptions, then *.*libs should be - // set to NULL or empty (this is why we check for the result being - // defined). + size_t p; + const string& t (pt != nullptr + ? ((p = pt->find (',')) == string::npos + ? *pt + : string (*pt, 0, p)) + : string ()); + + // Why are we bothering with impl for binless libraries since all + // their dependencies are by definition interface? Well, for one, it + // could be that it is dynamically-binless (e.g., binless on some + // platforms or in some configurations and binful on/in others). In + // this case it would be helpful to have a uniform semantics so that, + // for example, *.libs are used for liba{} regardless of whether it is + // binless or not. On the other hand, having to specify both + // *.export.libs=-lm and *.libs=-lm (or *.export.impl_libs) for an + // always-binless library is sure not very intuitive. Not sure if we + // can win here. // - c_e_libs = l[impl ? c_export_imp_libs : c_export_libs]; + bool impl (proc_impl && proc_impl (l, la)); + bool cc (false), same (false); - if (!cc) - x_e_libs = l[same - ? (impl ? x_export_imp_libs : x_export_libs) - : vp[*t + (impl ? ".export.imp_libs" : ".export.libs")]]; + if (!t.empty ()) + { + cc = (t == "cc"); + same = (!cc && t == x); + } - // Process options first. - // - if (proc_opt) + const scope& bs (t.empty () || cc ? top_bs : l.base_scope ()); + + lookup c_e_libs; + lookup x_e_libs; + + if (!t.empty ()) { - // If all we know is it's a C-common library, then in both cases we - // only look for cc.export.*. + // Note that we used to treat *.export.libs set on the liba/libs{} + // members as *.libs overrides rather than as member-specific + // interface dependencies. This difference in semantics proved to be + // surprising so now we have separate *.export.impl_libs for that. + // Note that in this case options come from *.export.* variables. + // + // Note also that we only check for *.*libs. If one doesn't have any + // libraries but needs to set, say, *.loptions, then *.*libs should + // be set to NULL or empty (this is why we check for the result + // being defined). + // + // Note: for performance we call lookup_original() directly (we know + // these variables are not overridable) and pass the base scope we + // have already resolved. + // + // See also deduplicate_export_libs() if changing anything here. // - if (cc) - proc_opt (l, *t, true, true); - else { - if (impl) + const variable& v (impl ? c_export_impl_libs : c_export_libs); + c_e_libs = l.lookup_original (v, false, &bs).first; + } + + if (!cc) + { + const variable& v ( + same + ? (impl ? x_export_impl_libs : x_export_libs) + : vp[t + (impl ? ".export.impl_libs" : ".export.libs")]); + x_e_libs = l.lookup_original (v, false, &bs).first; + } + + // Process options first. + // + if (proc_opt) + { + const target& ol (proc_opt_group && lg != nullptr ? *lg : l); + + // If all we know is it's a C-common library, then in both cases + // we only look for cc.export.*. + // + if (cc) { - // Interface and implementation: as discussed above, we can have - // two situations: overriden export or default export. - // - if (c_e_libs.defined () || x_e_libs.defined ()) + if (!proc_opt (ol, t, true, true)) break; + } + else + { + if (impl) { - // NOTE: should this not be from l.vars rather than l? Or - // perhaps we can assume non-common values will be set on - // libs{}/liba{}. + // Interface and implementation: as discussed above, we can + // have two situations: overriden export or default export. // - // Note: options come from *.export.* variables. - // - proc_opt (l, *t, false, true); - proc_opt (l, *t, true, true); + if (c_e_libs.defined () || x_e_libs.defined ()) + { + // NOTE: should this not be from l.vars rather than l? Or + // perhaps we can assume non-common values will be set on + // libs{}/liba{}. + // + // Note: options come from *.export.* variables. + // + if (!proc_opt (ol, t, false, true) || + !proc_opt (ol, t, true, true)) break; + } + else + { + // For default export we use the same options as were used + // to build the library. + // + if (!proc_opt (ol, t, false, false) || + !proc_opt (ol, t, true, false)) break; + } } else { - // For default export we use the same options as were used to - // build the library. + // Interface: only add *.export.* (interface dependencies). // - proc_opt (l, *t, false, false); - proc_opt (l, *t, true, false); + if (!proc_opt (ol, t, false, true) || + !proc_opt (ol, t, true, true)) break; } } - else - { - // Interface: only add *.export.* (interface dependencies). - // - proc_opt (l, *t, false, true); - proc_opt (l, *t, true, true); - } } } - } - - // Determine if an absolute path is to a system library. Note that - // we assume both paths to be normalized. - // - auto sys = [] (const dir_paths& sysd, const string& p) -> bool - { - size_t pn (p.size ()); - for (const dir_path& d: sysd) + // Determine if an absolute path is to a system library. Note that + // we assume both paths to be normalized. + // + auto sys = [] (const dir_paths& sysd, const string& p) -> bool { - const string& ds (d.string ()); // Can be "/", otherwise no slash. - size_t dn (ds.size ()); - - if (pn > dn && - p.compare (0, dn, ds) == 0 && - (path::traits_type::is_separator (ds[dn - 1]) || - path::traits_type::is_separator (p[dn]))) - return true; - } + size_t pn (p.size ()); - return false; - }; + for (const dir_path& d: sysd) + { + const string& ds (d.string ()); // Can be "/", otherwise no slash. + size_t dn (ds.size ()); + + if (pn > dn && + p.compare (0, dn, ds) == 0 && + (path::traits_type::is_separator (ds[dn - 1]) || + path::traits_type::is_separator (p[dn]))) + return true; + } - // Next process the library itself if requested. - // - if (self && proc_lib) - { - chain->push_back (&l); + return false; + }; - // Note that while normally the path is assigned, in case of an import - // stub the path to the DLL may not be known and so the path will be - // empty (but proc_lib() will use the import stub). + // Next process the library itself if requested. // - const path& p (l.path ()); - - bool s (t != nullptr // If cc library (matched or imported). - ? cast_false<bool> (l.vars[c_system]) - : !p.empty () && sys (top_sysd, p.string ())); + small_vector<reference_wrapper<const string>, 2> proc_lib_name;//Reuse. - proc_lib (&chain->back (), p.string (), lf, s); - } + if (self && proc_lib) + { + // Note that while normally the path is assigned, in case of an + // import stub the path to the DLL may not be known and so the path + // will be empty (but proc_lib() will use the import stub). + // + const file* f; + const path& p ((f = l.is_a<file> ()) ? f->path () : empty_path); - const scope& bs (t == nullptr || cc ? top_bs : l.base_scope ()); - optional<linfo> li; // Calculate lazily. - const dir_paths* sysd (nullptr); // Resolve lazily. + bool s (pt != nullptr // If cc library (matched or imported). + ? cast_false<bool> (l.vars[c_system]) + : !p.empty () && sys (top_sysd, p.string ())); - // Find system search directories corresponding to this library, i.e., - // from its project and for its type (C, C++, etc). - // - auto find_sysd = [&top_sysd, t, cc, same, &bs, &sysd, this] () - { - // Use the search dirs corresponding to this library scope/type. - // - sysd = (t == nullptr || cc) - ? &top_sysd // Imported library, use importer's sysd. - : &cast<dir_paths> ( - bs.root_scope ()->vars[same - ? x_sys_lib_dirs - : bs.ctx.var_pool[*t + ".sys_lib_dirs"]]); - }; + proc_lib_name = {p.string ()}; + if (!proc_lib (&chain->back (), proc_lib_name, lf, pt, s)) + break; + } - auto find_linfo = [top_li, t, cc, &bs, &l, &li] () - { - li = (t == nullptr || cc) - ? top_li - : link_info (bs, link_type (l).type); - }; + optional<optional<linfo>> li; // Calculate lazily. + const dir_paths* sysd (nullptr); // Resolve lazily. - // Only go into prerequisites (implementation) if instructed and we are - // not using explicit export. Otherwise, interface dependencies come - // from the lib{}:*.export.imp_libs below. - // - if (impl && !c_e_libs.defined () && !x_e_libs.defined ()) - { - for (const prerequisite_target& pt: l.prerequisite_targets[a]) + // Find system search directories corresponding to this library, i.e., + // from its project and for its type (C, C++, etc). + // + auto find_sysd = [&top_sysd, &vp, t, cc, same, &bs, &sysd, this] () { - // Note: adhoc prerequisites are not part of the library metadata - // protocol (and we should check for adhoc first to avoid races). + // Use the search dirs corresponding to this library scope/type. // - if (pt.adhoc || pt == nullptr) - continue; + sysd = (t.empty () || cc) + ? &top_sysd // Imported library, use importer's sysd. + : &cast<dir_paths> ( + bs.root_scope ()->vars[same + ? x_sys_lib_dirs + : vp[t + ".sys_lib_dirs"]]); + }; - bool la; - const file* f; + auto find_linfo = [top_li, t, cc, &bs, &l, &li] () + { + li = (t.empty () || cc) + ? top_li + : optional<linfo> (link_info (bs, link_type (l).type)); // @@ PERF + }; + + // Only go into prerequisites (implementation) if instructed and we + // are not using explicit export. Otherwise, interface dependencies + // come from the lib{}:*.export.impl_libs below. + // + if (impl && !c_e_libs.defined () && !x_e_libs.defined ()) + { +#if 0 + assert (top_li); // Must pick a member if implementation (see above). +#endif - if ((la = (f = pt->is_a<liba> ())) || - (la = (f = pt->is_a<libux> ())) || - ( f = pt->is_a<libs> ())) + for (const prerequisite_target& pt: l.prerequisite_targets[a]) { - if (sysd == nullptr) find_sysd (); - if (!li) find_linfo (); + // Note: adhoc prerequisites are not part of the library metadata + // protocol (and we should check for adhoc first to avoid races + // during execute). + // + if (pt.adhoc () || pt == nullptr) + continue; + + if (marked (pt)) + fail << "implicit dependency cycle detected involving library " + << l; + + bool la; + const file* f; + + if ((la = (f = pt->is_a<liba> ())) || + (la = (f = pt->is_a<libux> ())) || + ( f = pt->is_a<libs> ())) + { + // See link_rule for details. + // + const target* g ((pt.include & include_group) != 0 + ? f->group + : nullptr); + + if (sysd == nullptr) find_sysd (); + if (!li) find_linfo (); - process_libraries (a, bs, *li, *sysd, - *f, la, pt.data, - proc_impl, proc_lib, proc_opt, true, chain); + process_libraries_impl (a, bs, *li, *sysd, + g, *f, la, pt.data /* lflags */, + proc_impl, proc_lib, proc_opt, + true /* self */, proc_opt_group, + cache, chain, nullptr); + } } } - } - - // Process libraries (recursively) from *.export.*libs (of type names) - // handling import, etc. - // - // If it is not a C-common library, then it probably doesn't have any of - // the *.libs. - // - if (t != nullptr) - { - optional<dir_paths> usrd; // Extract lazily. - // Determine if a "simple path" is a system library. + // Process libraries (recursively) from *.export.*libs (of type names) + // handling import, etc. + // + // If it is not a C-common library, then it probably doesn't have any + // of the *.libs. // - auto sys_simple = [&sysd, &sys, &find_sysd] (const string& p) -> bool + if (!t.empty ()) { - bool s (!path::traits_type::absolute (p)); + optional<dir_paths> usrd; // Extract lazily. - if (!s) + // Determine if a "simple path" is a system library. + // + auto sys_simple = [&sysd, &sys, &find_sysd] (const string& p) -> bool { - if (sysd == nullptr) find_sysd (); + bool s (!path::traits_type::absolute (p)); - s = sys (*sysd, p); - } - - return s; - }; + if (!s) + { + if (sysd == nullptr) find_sysd (); + s = sys (*sysd, p); + } - auto proc_int = [&l, - &proc_impl, &proc_lib, &proc_opt, chain, - &sysd, &usrd, - &find_sysd, &find_linfo, &sys_simple, - &bs, a, &li, impl, this] (const lookup& lu) - { - const vector<name>* ns (cast_null<vector<name>> (lu)); - if (ns == nullptr || ns->empty ()) - return; + return s; + }; - for (auto i (ns->begin ()), e (ns->end ()); i != e; ++i) + // Determine the length of the library name fragment as well as + // whether it is a system library. Possible length values are: + // + // 1 - just the argument itself (-lm, -pthread) + // 2 - argument and next element (-l m, -framework CoreServices) + // 0 - unrecognized/until the end (-Wl,--whole-archive ...) + // + // See similar code in find_system_library(). + // + auto sense_fragment = [&sys_simple, this] (const string& l) -> + pair<size_t, bool> { - const name& n (*i); + size_t n; + bool s (true); - if (n.simple ()) + if (tsys == "win32-msvc") { - // This is something like -lpthread or shell32.lib so should be - // a valid path. But it can also be an absolute library path - // (e.g., something that may come from our .static/shared.pc - // files). - // - if (proc_lib) - proc_lib (nullptr, n.value, 0, sys_simple (n.value)); + if (l[0] == '/') + { + // Some other option (e.g., /WHOLEARCHIVE:<name>). + // + n = 0; + } + else + { + // Presumably a path. + // + n = 1; + s = sys_simple (l); + } } else { - // This is a potentially project-qualified target. - // - if (sysd == nullptr) find_sysd (); - if (!li) find_linfo (); + if (l[0] == '-') + { + // -l<name>, -l <name>, -pthread + // + if (l[1] == 'l' || l == "-pthread") + { + n = l.size () == 2 ? 2 : 1; + } + // -framework <name> (Mac OS) + // + else if (tsys == "darwin" && l == "-framework") + { + n = 2; + } + // Some other option (e.g., -Wl,--whole-archive). + // + else + n = 0; + } + else + { + // Presumably a path. + // + n = 1; + s = sys_simple (l); + } + } - const file& t ( - resolve_library (a, - bs, - n, - (n.pair ? (++i)->dir : dir_path ()), - *li, - *sysd, usrd)); + return make_pair (n, s); + }; + + auto proc_intf = [&l, proc_opt_group, cache, chain, + &proc_impl, &proc_lib, &proc_lib_name, &proc_opt, + &sysd, &usrd, + &find_sysd, &find_linfo, &sense_fragment, + &bs, a, &li, impl, this] ( + const lookup& lu, + small_vector<const target*, 32>* dedup, + size_t dedup_start) // Start of our deps. + { + const vector<name>* ns (cast_null<vector<name>> (lu)); + if (ns == nullptr || ns->empty ()) + return; + + for (auto i (ns->begin ()), e (ns->end ()); i != e; ) + { + const name& n (*i); - if (proc_lib) + // Note: see also recursively-binless logic in link_rule if + // changing anything in simple library handling. + // + if (n.simple ()) + { + // This is something like -lm or shell32.lib so should be a + // valid path. But it can also be an absolute library path + // (e.g., something that may come from our .{static/shared}.pc + // files). + // + if (proc_lib) + { + pair<size_t, bool> r (sense_fragment (n.value)); + + proc_lib_name.clear (); + for (auto e1 (r.first != 0 ? i + r.first : e); + i != e && i != e1 && i->simple (); + ++i) + { + proc_lib_name.push_back (i->value); + } + + proc_lib (nullptr, proc_lib_name, 0, nullptr, r.second); + continue; + } + } + else { + // This is a potentially project-qualified target. + // + if (sysd == nullptr) find_sysd (); + if (!li) find_linfo (); + + const mtime_target* t; + const target* g; + + const char* w (nullptr); + try + { + pair<const mtime_target&, const target*> p ( + resolve_library (a, + bs, + n, + (n.pair ? (++i)->dir : dir_path ()), + *li, + *sysd, usrd, + cache)); + + t = &p.first; + g = p.second; + + // Deduplicate. + // + // Note that dedup_start makes sure we only consider our + // interface dependencies while maintaining the "through" + // list. + // + if (dedup != nullptr) + { + if (find (dedup->begin () + dedup_start, + dedup->end (), + t) != dedup->end ()) + { + ++i; + continue; + } + + dedup->push_back (t); + } + } + catch (const non_existent_library& e) + { + // This is another manifestation of the "mentioned in + // *.export.libs but not in prerequisites" case (see below). + // + t = &e.target; + w = "unknown"; + } + // This can happen if the target is mentioned in *.export.libs // (i.e., it is an interface dependency) but not in the // library's prerequisites (i.e., it is not an implementation @@ -335,87 +616,193 @@ namespace build2 // on Windows import-installed DLLs may legally have empty // paths. // - if (t.mtime () == timestamp_unknown) + if (w != nullptr) + ; // See above. + else if (l.ctx.phase == run_phase::match) + { + // We allow not matching installed libraries if all we need + // is their options (see compile_rule::apply()). + // + if (proc_lib || t->base_scope ().root_scope () != nullptr) + { + if (!t->matched (a)) + w = "not matched"; + } + } + else + { + // Note that this check we only do if there is proc_lib + // (since it's valid to process library's options before + // updating it). + // + if (proc_lib) + { + if (t->mtime () == timestamp_unknown) + w = "out of date"; + } + } + + if (w != nullptr) + { fail << (impl ? "implementation" : "interface") - << " dependency " << t << " is out of date" << - info << "mentioned in *.export." << (impl ? "imp_" : "") + << " dependency " << *t << " is " << w << + info << "mentioned in *.export." << (impl ? "impl_" : "") << "libs of target " << l << - info << "is it a prerequisite of " << l << "?"; + info << "is it a prerequisite of " << l << "?" << endf; + } + + // Process it recursively. + // + bool u; + bool la ((u = t->is_a<libux> ()) || t->is_a<liba> ()); + lflags lf (0); + + // If this is a static library, see if we need to link it + // whole. + // + if (la && proc_lib) + { + // Note: go straight for the public variable pool. + // + const variable& var (t->ctx.var_pool["bin.whole"]); + + // See the link rule for the lookup semantics. + // + lookup l ( + t->lookup_original (var, true /* target_only */).first); + + if (l ? cast<bool> (*l) : u) + lf |= lflag_whole; + } + + process_libraries_impl ( + a, bs, *li, *sysd, + g, *t, la, lf, + proc_impl, proc_lib, proc_opt, + true /* self */, proc_opt_group, + cache, chain, dedup); } - // Process it recursively. - // - // @@ Where can we get the link flags? Should we try to find - // them in the library's prerequisites? What about installed - // stuff? - // - process_libraries (a, bs, *li, *sysd, - t, t.is_a<liba> () || t.is_a<libux> (), 0, - proc_impl, proc_lib, proc_opt, true, chain); + ++i; } - } - }; + }; - // Process libraries from *.libs (of type strings). - // - auto proc_imp = [&proc_lib, &sys_simple] (const lookup& lu) - { - const strings* ns (cast_null<strings> (lu)); - if (ns == nullptr || ns->empty ()) - return; + auto proc_intf_storage = [&proc_intf] (const lookup& lu1, + const lookup& lu2 = lookup ()) + { + small_vector<const target*, 32> dedup_storage; - for (const string& n: *ns) + if (lu1) proc_intf (lu1, &dedup_storage, 0); + if (lu2) proc_intf (lu2, &dedup_storage, 0); + }; + + // Process libraries from *.libs (of type strings). + // + auto proc_impl = [&proc_lib, &proc_lib_name, + &sense_fragment] (const lookup& lu) { - // This is something like -lpthread or shell32.lib so should be a - // valid path. - // - proc_lib (nullptr, n, 0, sys_simple (n)); - } - }; + const strings* ns (cast_null<strings> (lu)); + if (ns == nullptr || ns->empty ()) + return; - // Note: the same structure as when processing options above. - // - // If all we know is it's a C-common library, then in both cases we - // only look for cc.export.*libs. - // - if (cc) - { - if (c_e_libs) proc_int (c_e_libs); - } - else - { - if (impl) + for (auto i (ns->begin ()), e (ns->end ()); i != e; ) + { + // This is something like -lm or shell32.lib so should be a + // valid path. + // + pair<size_t, bool> r (sense_fragment (*i)); + + proc_lib_name.clear (); + for (auto e1 (r.first != 0 ? i + r.first : e); + i != e && i != e1; + ++i) + { + proc_lib_name.push_back (*i); + } + + proc_lib (nullptr, proc_lib_name, 0, nullptr, r.second); + } + }; + + // Note: the same structure as when processing options above. + // + // If all we know is it's a C-common library, then in both cases we + // only look for cc.export.*libs. + // + if (cc) { - // Interface and implementation: as discussed above, we can have - // two situations: overriden export or default export. - // - if (c_e_libs.defined () || x_e_libs.defined ()) + if (impl) { - if (c_e_libs) proc_int (c_e_libs); - if (x_e_libs) proc_int (x_e_libs); + if (c_e_libs) proc_intf (c_e_libs, nullptr, 0); } else { - // For default export we use the same options/libs as were used - // to build the library. Since libraries in (non-export) *.libs - // are not targets, we don't need to recurse. - // - if (proc_lib) + if (c_e_libs) { - proc_imp (l[c_libs]); - proc_imp (l[same ? x_libs : vp[*t + ".libs"]]); + if (dedup != nullptr) + proc_intf (c_e_libs, dedup, dedup->size ()); + else + proc_intf_storage (c_e_libs); } } } else { - // Interface: only add *.export.* (interface dependencies). - // - if (c_e_libs) proc_int (c_e_libs); - if (x_e_libs) proc_int (x_e_libs); + // Note: see also recursively-binless logic in link_rule if + // changing anything here. + + if (impl) + { + // Interface and implementation: as discussed above, we can have + // two situations: overriden export or default export. + // + if (c_e_libs.defined () || x_e_libs.defined ()) + { + // Why are we calling proc_intf() on *.impl_libs? Perhaps + // because proc_impl() expects strings, not names? Yes, and + // proc_intf() checks impl. + // + if (c_e_libs) proc_intf (c_e_libs, nullptr, 0); + if (x_e_libs) proc_intf (x_e_libs, nullptr, 0); + } + else + { + // For default export we use the same options/libs as were + // used to build the library. Since libraries in (non-export) + // *.libs are not targets, we don't need to recurse. + // + // Note: for performance we call lookup_original() directly + // (we know these variables are not overridable) and pass the + // base scope we have already resolved. + // + if (proc_lib) + { + const variable& v (same ? x_libs : vp[t + ".libs"]); + proc_impl (l.lookup_original (c_libs, false, &bs).first); + proc_impl (l.lookup_original (v, false, &bs).first); + } + } + } + else + { + // Interface: only add *.export.* (interface dependencies). + // + if (c_e_libs.defined () || x_e_libs.defined ()) + { + if (dedup != nullptr) + { + size_t s (dedup->size ()); // Start of our interface deps. + + if (c_e_libs) proc_intf (c_e_libs, dedup, s); + if (x_e_libs) proc_intf (x_e_libs, dedup, s); + } + else + proc_intf_storage (c_e_libs, x_e_libs); + } + } } } - } + } while (false); // Breakout loop end. // Remove this library from the chain. // @@ -436,21 +823,69 @@ namespace build2 // will select exactly the same target as the library's matched rule and // that's the only way to guarantee it will be up-to-date. // - const file& common:: + // If li is absent, then don't pick the liba/libs{} member, returning the + // lib{} target itself. If li is present, then the returned target is + // always a file. The second half of the returned pair is the group, if + // the member was picked. + // + // Note: paths in sysd/usrd are expected to be absolute and normalized. + // + // Note: may throw non_existent_library. + // + pair<const mtime_target&, const target*> common:: resolve_library (action a, const scope& s, const name& cn, const dir_path& out, - linfo li, + optional<linfo> li, const dir_paths& sysd, - optional<dir_paths>& usrd) const + optional<dir_paths>& usrd, + library_cache* cache) const { + bool q (cn.qualified ()); + auto lo (li ? optional<lorder> (li->order) : nullopt); + + // If this is an absolute and normalized unqualified name (which means + // the scope does not factor into the result), then first check the + // cache. + // + // Note that normally we will have a handful of libraries repeated a + // large number of times (see Boost for an extreme example of this). + // + // Note also that for non-utility libraries we know that only the link + // order from linfo is used. While not caching it and always picking an + // alternative could also work, we cache it to avoid the lookup. + // + if (cache != nullptr) + { + if (!q && + (cn.dir.absolute () && cn.dir.normalized ()) && + (out.empty () || (out.absolute () && out.normalized ()))) + { + auto i (find_if (cache->begin (), cache->end (), + [lo, &cn, &out] (const library_cache_entry& e) + { + const target& t (e.lib); + return (e.lo == lo && + e.value == cn.value && + e.type == cn.type && + t.dir == cn.dir && + t.out == out); + })); + + if (i != cache->end ()) + return pair<const mtime_target&, const target*> {i->lib, i->group}; + } + else + cache = nullptr; // Do not cache. + } + if (cn.type != "lib" && cn.type != "liba" && cn.type != "libs") fail << "target name " << cn << " is not a library"; const target* xt (nullptr); - if (!cn.qualified ()) + if (!q) { // Search for an existing target with this name "as if" it was a // prerequisite. @@ -478,18 +913,36 @@ namespace build2 fail << "unable to find library " << pk; } - // If this is lib{}/libu*{}, pick appropriate member. + // If this is lib{}/libul{}, pick appropriate member unless we were + // instructed not to. // - if (const libx* l = xt->is_a<libx> ()) - xt = link_member (*l, a, li); // Pick lib*{e,a,s}{}. + const target* g (nullptr); + if (li) + { + if (const libx* l = xt->is_a<libx> ()) + { + g = xt; + xt = link_member (*l, a, *li); // Pick lib*{e,a,s}{}. + } + } + + auto& t (xt->as<mtime_target> ()); - return xt->as<file> (); + if (cache != nullptr) + cache->push_back (library_cache_entry {lo, cn.type, cn.value, t, g}); + + return pair<const mtime_target&, const target*> {t, g}; } - // Note that pk's scope should not be NULL (even if dir is absolute). + // Action should be absent if called during the load phase. Note that pk's + // scope should not be NULL (even if dir is absolute). + // + // Note: paths in sysd/usrd are expected to be absolute and normalized. + // + // Note: see similar logic in find_system_library(). // target* common:: - search_library (action act, + search_library (optional<action> act, const dir_paths& sysd, optional<dir_paths>& usrd, const prerequisite_key& p, @@ -497,7 +950,7 @@ namespace build2 { tracer trace (x, "search_library"); - assert (p.scope != nullptr); + assert (p.scope != nullptr && (!exist || act)); context& ctx (p.scope->ctx); const scope& rs (*p.scope->root_scope ()); @@ -614,6 +1067,21 @@ namespace build2 { context& ctx (p.scope->ctx); + // Whether to look for a binless variant using the common .pc file + // (see below). + // + // Normally we look for a binless version if the binful one was not + // found. However, sometimes we may find what looks like a binful + // library but on a closer examination realize that there is something + // wrong with it (for example, it's not a Windows import library). In + // such cases we want to omit looking for a binless library using the + // common .pc file since it most likely corresponds to the binful + // library (and we may end up in a infinite loop trying to resolve + // itself). + // + bool ba (true); + bool bs (true); + timestamp mt; // libs @@ -685,6 +1153,31 @@ namespace build2 s->path_mtime (move (f), mt); } } + else if (!ext && tsys == "darwin") + { + // Besides .dylib, Mac OS now also has "text-based stub libraries" + // that use the .tbd extension. They appear to be similar to + // Windows import libraries and contain information such as the + // location of the .dylib library, its symbols, etc. For example, + // there is /Library/.../MacOSX13.3.sdk/usr/lib/libsqlite3.tbd + // which points to /usr/lib/libsqlite3.dylib (but which itself is + // invisible/inaccessible, presumably for security). + // + // Note that for now we are treating the .tbd library as the + // shared library but could probably do the more elaborate dance + // with ad hoc members like on Windows if really necessary. + // + se = string ("tbd"); + f = f.base (); // Remove .dylib. + f += ".tbd"; + mt = mtime (f); + + if (mt != timestamp_nonexistent) + { + insert_library (ctx, s, name, d, ld, se, exist, trace); + s->path_mtime (move (f), mt); + } + } } // liba @@ -714,10 +1207,24 @@ namespace build2 if (tsys == "win32-msvc") { if (s == nullptr && !sn.empty ()) - s = msvc_search_shared (ld, d, p, exist); + { + pair<libs*, bool> r (msvc_search_shared (ld, d, p, exist)); + + if (r.first != nullptr) + s = r.first; + else if (!r.second) + bs = false; + } if (a == nullptr && !an.empty ()) - a = msvc_search_static (ld, d, p, exist); + { + pair<liba*, bool> r (msvc_search_static (ld, d, p, exist)); + + if (r.first != nullptr) + a = r.first; + else if (!r.second) + ba = false; + } } // Look for binary-less libraries via pkg-config .pc files. Note that @@ -734,7 +1241,10 @@ namespace build2 // is no binful variant. // pair<path, path> r ( - pkgconfig_search (d, p.proj, name, na && ns /* common */)); + pkgconfig_search (d, + p.proj, + name, + na && ns && ba && bs /* common */)); if (na && !r.first.empty ()) { @@ -787,6 +1297,8 @@ namespace build2 // making it the only one to allow things to be overriden (e.g., // if build2 was moved or some such). // + // Note: build_install_lib is already normalized. + // usrd->insert (usrd->begin (), build_install_lib); } } @@ -839,43 +1351,88 @@ namespace build2 if (exist) return r; - // If we cannot acquire the lock then this mean the target has already - // been matched and we assume all of this has already been done. + // Try to extract library information from pkg-config. We only add the + // default macro if we could not extract more precise information. The + // idea is that in .pc files that we generate, we copy those macros (or + // custom ones) from *.export.poptions. // - auto lock = [act] (const target* t) -> target_lock + // @@ Should we add .pc files as ad hoc members so pkgconfig_save() can + // use their names when deriving -l-names (this would be especially + // helpful for binless libraries to get hold of prefix/suffix, etc). + // + auto load_pc = [this, &trace, + act, &p, &name, + &sysd, &usrd, + pd, &pc, lt, a, s] (pair<bool, bool> metaonly) { - auto l (t != nullptr ? build2::lock (act, *t, true) : target_lock ()); - - if (l && l.offset == target::offset_matched) + l5 ([&]{trace << "loading pkg-config information during " + << (act ? "match" : "load") << " for " + << (a != nullptr ? "static " : "") + << (s != nullptr ? "shared " : "") + << "member(s) of " << *lt << "; metadata only: " + << metaonly.first << " " << metaonly.second;}); + + // Add the "using static/shared library" macro (used, for example, to + // handle DLL export). The absence of either of these macros would + // mean some other build system that cannot distinguish between the + // two (and no pkg-config information). + // + auto add_macro = [this] (target& t, const char* suffix) { - assert ((*t)[act].rule == &file_rule::rule_match); - l.unlock (); - } - - return l; - }; + // If there is already a value (either in cc.export or x.export), + // don't add anything: we don't want to be accumulating defines nor + // messing with custom values. And if we are adding, then use the + // generic cc.export. + // + // The only way we could already have this value is if this same + // library was also imported as a project (as opposed to installed). + // Unlikely but possible. In this case the values were set by the + // export stub and we shouldn't touch them. + // + if (!t.vars[x_export_poptions]) + { + auto p (t.vars.insert (c_export_poptions)); - target_lock ll (lock (lt)); + if (p.second) + { + // The "standard" macro name will be LIB<NAME>_{STATIC,SHARED}, + // where <name> is the target name. Here we want to strike a + // balance between being unique and not too noisy. + // + string d ("-DLIB"); - // Set lib{} group members to indicate what's available. Note that we - // must be careful here since its possible we have already imported some - // of its members. - // - timestamp mt (timestamp_nonexistent); - if (ll) - { - if (s != nullptr) {lt->s = s; mt = s->mtime ();} - if (a != nullptr) {lt->a = a; mt = a->mtime ();} - } + d += sanitize_identifier ( + ucase (const_cast<const string&> (t.name))); - target_lock al (lock (a)); - target_lock sl (lock (s)); + d += '_'; + d += suffix; - if (!al) a = nullptr; - if (!sl) s = nullptr; + strings o; + o.push_back (move (d)); + p.first = move (o); + } + } + }; - if (a != nullptr) a->group = lt; - if (s != nullptr) s->group = lt; + if (pc.first.empty () && pc.second.empty ()) + { + if (!pkgconfig_load (act, *p.scope, + *lt, a, s, + p.proj, name, + *pd, sysd, *usrd, + metaonly)) + { + if (a != nullptr && !metaonly.first) add_macro (*a, "STATIC"); + if (s != nullptr && !metaonly.second) add_macro (*s, "SHARED"); + } + } + else + pkgconfig_load (act, *p.scope, + *lt, a, s, + pc, + *pd, sysd, *usrd, + metaonly); + }; // Mark as a "cc" library (unless already marked) and set the system // flag. @@ -886,7 +1443,7 @@ namespace build2 if (p.second) { - p.first.get () = string ("cc"); + p.first = string ("cc"); if (sys) t.vars.assign (c_system) = true; @@ -895,74 +1452,129 @@ namespace build2 return p.second; }; - // If the library already has cc.type, then assume it was either - // already imported or was matched by a rule. + // Deal with the load phase case. The rest is already hairy enough so + // let's not try to weave this logic into that. // - if (a != nullptr && !mark_cc (*a)) a = nullptr; - if (s != nullptr && !mark_cc (*s)) s = nullptr; - - // Add the "using static/shared library" macro (used, for example, to - // handle DLL export). The absence of either of these macros would - // mean some other build system that cannot distinguish between the - // two (and no pkg-config information). - // - auto add_macro = [this] (target& t, const char* suffix) + if (!act) { - // If there is already a value (either in cc.export or x.export), - // don't add anything: we don't want to be accumulating defines nor - // messing with custom values. And if we are adding, then use the - // generic cc.export. + assert (ctx.phase == run_phase::load); + + // The overall idea here is to set everything up so that the default + // file_rule matches the returned targets, the same way as it would if + // multiple operations were executed for the match phase (see below). // - // The only way we could already have this value is if this same - // library was also imported as a project (as opposed to installed). - // Unlikely but possible. In this case the values were set by the - // export stub and we shouldn't touch them. + // Note however, that there is no guarantee that we won't end up in + // the match phase code below even after loading things here. For + // example, the same library could be searched from pkgconfig_load() + // if specified as -l. And if we try to re-assign group members, then + // that would be a race condition. So we use the cc mark to detect + // this. // - if (!t.vars[x_export_poptions]) + timestamp mt (timestamp_nonexistent); + if (a != nullptr) {lt->a = a; a->group = lt; mt = a->mtime ();} + if (s != nullptr) {lt->s = s; s->group = lt; mt = s->mtime ();} + + // @@ TODO: we currently always reload pkgconfig for lt (and below). + // + mark_cc (*lt); + lt->mtime (mt); // Note: problematic, see below for details. + + // We can only load metadata from here since we can only do this + // during the load phase. But it's also possible that a racing match + // phase already found and loaded this library without metadata. So + // looks like the only way is to load the metadata incrementally. We + // can base this decision on the presense/absense of cc.type and + // export.metadata. + // + pair<bool, bool> metaonly {false, false}; + + if (a != nullptr && !mark_cc (*a)) { - auto p (t.vars.insert (c_export_poptions)); + if (a->vars[ctx.var_export_metadata]) + a = nullptr; + else + metaonly.first = true; + } - if (p.second) - { - // The "standard" macro name will be LIB<NAME>_{STATIC,SHARED}, - // where <name> is the target name. Here we want to strike a - // balance between being unique and not too noisy. - // - string d ("-DLIB"); + if (s != nullptr && !mark_cc (*s)) + { + if (s->vars[ctx.var_export_metadata]) + s = nullptr; + else + metaonly.second = true; + } - d += sanitize_identifier ( - ucase (const_cast<const string&> (t.name))); + // Try to extract library information from pkg-config. + // + if (a != nullptr || s != nullptr) + load_pc (metaonly); + + return r; + } - d += '_'; - d += suffix; + // If we cannot acquire the lock then this mean the target has already + // been matched and we assume all of this has already been done. + // + auto lock = [a = *act] (const target* t) -> target_lock + { + auto l (t != nullptr ? build2::lock (a, *t, true) : target_lock ()); - strings o; - o.push_back (move (d)); - p.first.get () = move (o); - } + if (l && l.offset == target::offset_matched) + { + assert ((*t)[a].rule == &file_rule::rule_match); + l.unlock (); } + + return l; }; - if (ll && (a != nullptr || s != nullptr)) + target_lock al (lock (a)); + target_lock sl (lock (s)); + + target_lock ll (lock (lt)); + + // Set lib{} group members to indicate what's available. Note that we + // must be careful here since its possible we have already imported some + // of its members. + // + timestamp mt (timestamp_nonexistent); + if (ll) { - // Try to extract library information from pkg-config. We only add the - // default macro if we could not extract more precise information. The - // idea is that in .pc files that we generate, we copy those macros - // (or custom ones) from *.export.poptions. + // Mark the group since sometimes we use it itself instead of one of + // the liba/libs{} members (see process_libraries_impl() for details). // - if (pc.first.empty () && pc.second.empty ()) + // If it's already marked, then it could have been imported during + // load (see above). + // + // @@ TODO: we currently always reload pkgconfig for lt (and above). + // Maybe pass NULL lt to pkgconfig_load() in this case? + // + if (mark_cc (*lt)) { - if (!pkgconfig_load (act, *p.scope, - *lt, a, s, - p.proj, name, - *pd, sysd, *usrd)) - { - if (a != nullptr) add_macro (*a, "STATIC"); - if (s != nullptr) add_macro (*s, "SHARED"); - } + if (a != nullptr) {lt->a = a; mt = a->mtime ();} + if (s != nullptr) {lt->s = s; mt = s->mtime ();} } else - pkgconfig_load (act, *p.scope, *lt, a, s, pc, *pd, sysd, *usrd); + ll.unlock (); + } + + if (!al) a = nullptr; + if (!sl) s = nullptr; + + // If the library already has cc.type, then assume it was either already + // imported (e.g., during load) or was matched by a rule. + // + if (a != nullptr && !mark_cc (*a)) a = nullptr; + if (s != nullptr && !mark_cc (*s)) s = nullptr; + + if (a != nullptr) a->group = lt; + if (s != nullptr) s->group = lt; + + if (ll && (a != nullptr || s != nullptr)) + { + // Try to extract library information from pkg-config. + // + load_pc ({false, false} /* metaonly */); } // If we have the lock (meaning this is the first time), set the matched @@ -975,10 +1587,38 @@ namespace build2 // // Note also that these calls clear target data. // - if (al) match_rule (al, file_rule::rule_match); - if (sl) match_rule (sl, file_rule::rule_match); + if (a != nullptr) match_rule (al, file_rule::rule_match); + if (s != nullptr) match_rule (sl, file_rule::rule_match); if (ll) { + // @@ Turns out this has a problem: file_rule won't match/execute + // group members. So what happens is that if we have two installed + // libraries, say lib{build2} that depends on lib{butl}, then + // lib{build2} will have lib{butl} as a prerequisite and file_rule + // that matches lib{build2} will update lib{butl} (also matched by + // file_rule), but not its members. Later, someone (for example, + // the newer() call in append_libraries()) will pick one of the + // members assuming it is executed and things will go sideways. + // + // For now we hacked around the issue but the long term solution is + // probably to add to the bin module a special rule that is + // registered on the global scope and matches the installed lib{} + // targets. This rule will have to both update prerequisites like + // the file_rule and group members like the lib_rule (or maybe it + // can skip prerequisites since one of the member will do that; in + // which case maybe we will be able to reuse lib_rule maybe with + // the "all members" flag or some such). A few additional + // notes/thoughts: + // + // - Will be able to stop inheriting lib{} from mtime_target. + // + // - Will need to register for perform_update/clean like in context + // as well as for configure as in the config module (feels like + // shouldn't need to register for dist). + // + // - Will need to test batches, immediate import thoroughly (this + // stuff is notoriously tricky to get right in all situations). + // match_rule (ll, file_rule::rule_match); // Also bless the library group with a "trust me it exists" timestamp. @@ -987,6 +1627,8 @@ namespace build2 // won't match. // lt->mtime (mt); + + ll.unlock (); // Unlock group before members, for good measure. } return r; @@ -1028,5 +1670,85 @@ namespace build2 return r; } + + void common:: + append_diag_color_options (cstrings& args) const + { + switch (cclass) + { + case compiler_class::msvc: + { + // MSVC has the /diagnostics: option which has an undocumented value + // `color`. It's unclear from which version of MSVC this value is + // supported, but it works in 17.0, so let's start from there. + // + // Note that there is currently no way to disable color in the MSVC + // diagnostics specifically (the /diagnostics:* option values are + // cumulative and there doesn't seem to be a `color-` value). This + // is probably not a big deal since one can just disable the color + // globally (--no-diag-color). + // + // Note that clang-cl appears to use -fansi-escape-codes. See GH + // issue #312 for background. + // + if (show_diag_color ()) + { + if (cvariant.empty () && + (cmaj > 19 || (cmaj == 19 && cmin >= 30))) + { + // Check for the prefix in case /diagnostics:color- gets added + // eventually. + // + if (!find_option_prefixes ({"/diagnostics:color", + "-diagnostics:color"}, args)) + { + args.push_back ("/diagnostics:color"); + } + } + } + + break; + } + case compiler_class::gcc: + { + // Enable/disable diagnostics color unless a custom option is + // specified. + // + // Supported from GCC 4.9 (8.1 on Windows) and (at least) from Clang + // 3.5. Clang supports -f[no]color-diagnostics in addition to the + // GCC's spelling. + // + if ( +#ifndef _WIN32 + ctype == compiler_type::gcc ? cmaj > 4 || (cmaj == 4 && cmin >= 9) : +#else + ctype == compiler_type::gcc ? cmaj > 8 || (cmaj == 8 && cmin >= 1) : +#endif + ctype == compiler_type::clang ? cmaj > 3 || (cmaj == 3 && cmin >= 5) : + false) + { + if (!(find_option_prefix ("-fdiagnostics-color", args) || + find_option ("-fno-diagnostics-color", args) || + find_option ("-fdiagnostics-plain-output", args) || + (ctype == compiler_type::clang && + (find_option ("-fcolor-diagnostics", args) || + find_option ("-fno-color-diagnostics", args))))) + { + // Omit -fno-diagnostics-color if stderr is not a terminal (we + // know there will be no color in this case and the option will + // just add noise, for example, in build logs). + // + if (const char* o = ( + show_diag_color () ? "-fdiagnostics-color" : + stderr_term ? "-fno-diagnostics-color" : + nullptr)) + args.push_back (o); + } + } + + break; + } + } + } } } |