// file : libbuild2/cc/common.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include <libbuild2/cc/common.hxx> #include <libbuild2/file.hxx> // import() #include <libbuild2/scope.hxx> #include <libbuild2/variable.hxx> #include <libbuild2/algorithm.hxx> #include <libbuild2/filesystem.hxx> #include <libbuild2/diagnostics.hxx> #include <libbuild2/cc/utility.hxx> using namespace std; using namespace butl; namespace build2 { namespace cc { using namespace bin; // Recursively process prerequisite libraries. If proc_impl returns false, // then only process interface (*.export.libs), otherwise -- interface and // implementation (prerequisite and from *.libs, unless overriden). // // Note that here we assume that an interface library is also an // implementation (since we don't use *.export.libs in static link). We // currently have this restriction to make sure the target in // *.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) // 2. lib itself (if self is true) // 3. dependency libs (prerequisite_targets, left to right, depth-first) // 4. dependency libs (*.libs variables). // // 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 passes to process_libraries(). The first element of this // array is NULL. // void common:: process_libraries ( action a, const scope& top_bs, linfo top_li, const dir_paths& top_sysd, const file& l, 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 { small_vector<const file*, 16> chain_storage; if (chain == nullptr) { chain = &chain_storage; chain->push_back (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). // const string* t (cast_null<string> (l.state[a][c_type])); bool impl (proc_impl && proc_impl (l, la)); bool cc (false), same (false); auto& vp (top_bs.ctx.var_pool); lookup c_e_libs; lookup x_e_libs; if (t != nullptr) { cc = *t == "cc"; same = !cc && *t == x; // The explicit export override should be set on the liba/libs{} // target itself. 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). // if (impl) c_e_libs = l.vars[c_export_libs]; // Override. else if (l.group != nullptr) // lib{} group. c_e_libs = l.group->vars[c_export_libs]; if (!cc) { const variable& var (same ? x_export_libs : vp[*t + ".export.libs"]); if (impl) x_e_libs = l.vars[var]; // Override. else if (l.group != nullptr) // lib{} group. x_e_libs = l.group->vars[var]; } // Process options first. // if (proc_opt) { // If all we know is it's a C-common library, then in both cases we // only look for cc.export.*. // if (cc) proc_opt (l, *t, true, true); else { 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 ()) { // 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{}. // proc_opt (l, *t, false, true); proc_opt (l, *t, true, true); } else { // For default export we use the same options as were used to // build the library. // proc_opt (l, *t, false, false); proc_opt (l, *t, true, false); } } 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) { 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; } return false; }; // Next process the library itself if requested. // if (self && proc_lib) { chain->push_back (&l); // 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 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 ())); proc_lib (&chain->back (), p.string (), lf, s); } const scope& bs (t == nullptr || cc ? top_bs : l.base_scope ()); optional<linfo> li; // Calculate lazily. const dir_paths* sysd (nullptr); // Resolve lazily. // 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"]]); }; auto find_linfo = [top_li, t, cc, &bs, &l, &li] () { li = (t == nullptr || cc) ? top_li : link_info (bs, link_type (l).type); }; // Only go into prerequisites (implementation) if instructed and we are // not using explicit export. Otherwise, interface dependencies come // from the lib{}:*.export.libs below. // if (impl && !c_e_libs.defined () && !x_e_libs.defined ()) { for (const prerequisite_target& pt: l.prerequisite_targets[a]) { // Note: adhoc prerequisites are not part of the library meta- // information protocol. // if (pt == nullptr || pt.adhoc) continue; bool la; const file* f; if ((la = (f = pt->is_a<liba> ())) || (la = (f = pt->is_a<libux> ())) || ( f = pt->is_a<libs> ())) { 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 (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. // auto sys_simple = [&sysd, &sys, &find_sysd] (const string& p) -> bool { bool s (!path::traits_type::absolute (p)); if (!s) { if (sysd == nullptr) find_sysd (); s = sys (*sysd, p); } return s; }; auto proc_int = [&l, &proc_impl, &proc_lib, &proc_opt, chain, &sysd, &usrd, &find_sysd, &find_linfo, &sys_simple, &bs, a, &li, this] (const lookup& lu) { 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; ++i) { const name& n (*i); if (n.simple ()) { // 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)); } else { // This is a potentially project-qualified target. // if (sysd == nullptr) find_sysd (); if (!li) find_linfo (); const file& t ( resolve_library (a, bs, n, (n.pair ? (++i)->dir : dir_path ()), *li, *sysd, usrd)); if (proc_lib) { // 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 // dependency). // // Note that we used to just check for path being assigned but // on Windows import-installed DLLs may legally have empty // paths. // if (t.mtime () == timestamp_unknown) fail << "interface dependency " << t << " is out of date" << info << "mentioned in *.export.libs of target " << l << info << "is it a prerequisite of " << l << "?"; } // 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); } } }; // 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; for (const string& n: *ns) { // This is something like -lpthread or shell32.lib so should be a // valid path. // proc_lib (nullptr, n, 0, sys_simple (n)); } }; // 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) { // 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 (c_e_libs) proc_int (c_e_libs); if (x_e_libs) proc_int (x_e_libs); } 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) { proc_imp (l[c_libs]); proc_imp (l[same ? x_libs : vp[*t + ".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); } } } // Remove this library from the chain. // if (self && proc_lib) chain->pop_back (); } // The name can be an absolute or relative target name (for example, // /tmp/libfoo/lib{foo} or ../libfoo/lib{foo}) or a project-qualified // relative target name (e.g., libfoo%lib{foo}). // // Note that in case of the relative target that comes from export.libs, // the resolution happens relative to the base scope of the target from // which this export.libs came, which is exactly what we want. // // Note that the scope, search paths, and the link order should all be // derived from the library target that mentioned this name. This way we // 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:: resolve_library (action a, const scope& s, const name& cn, const dir_path& out, linfo li, const dir_paths& sysd, optional<dir_paths>& usrd) const { 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 ()) { // Search for an existing target with this name "as if" it was a // prerequisite. // xt = search_existing (cn, s, out); if (xt == nullptr) fail << "unable to find library " << cn; } else { // This is import. // name n (cn); auto rp (s.find_target_type (n, location ())); // Note: changes name. const target_type* tt (rp.first); optional<string>& ext (rp.second); if (tt == nullptr) fail << "unknown target type '" << n.type << "' in library " << n; // @@ OUT: for now we assume out is undetermined, just like in // search (name, scope). // dir_path out; prerequisite_key pk {n.proj, {tt, &n.dir, &out, &n.value, ext}, &s}; xt = search_library_existing (a, sysd, usrd, pk); if (xt == nullptr) { if (n.qualified ()) xt = import_existing (s.ctx, pk); } if (xt == nullptr) fail << "unable to find library " << pk; } // If this is lib{}/libu*{}, pick appropriate member. // if (const libx* l = xt->is_a<libx> ()) xt = link_member (*l, a, li); // Pick lib*{e,a,s}{}. return xt->as<file> (); } // Insert a target "tagging" it with the specified process path and // verifying that it already exists if requested. Return the lock. // template <typename T> ulock common:: insert_library (context& ctx, T*& r, string name, dir_path dir, const process_path& out, optional<string> ext, bool exist, tracer& trace) { auto p (ctx.targets.insert_locked (T::static_type, move (dir), path_cast<dir_path> (out.effect), name, move (ext), true, // Implied. trace)); assert (!exist || !p.second.owns_lock ()); r = &p.first.template as<T> (); return move (p.second); } // Note that pk's scope should not be NULL (even if dir is absolute). // target* common:: search_library (action act, const dir_paths& sysd, optional<dir_paths>& usrd, const prerequisite_key& p, bool exist) const { tracer trace (x, "search_library"); assert (p.scope != nullptr); context& ctx (p.scope->ctx); const scope& rs (*p.scope->root_scope ()); // Here is the problem: we may be building for two different toolchains // simultaneously that use the same installed library. But our search is // toolchain-specific. To make sure we end up with different targets for // each toolchain we are going to "tag" each target with the linker path // as its out directory. // const process_path& ld (tsys != "win32-msvc" ? cpath : cast<process_path> (rs["bin.ld.path"])); // @@ 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<lib> ()); const optional<string>& ext (l ? nullopt : p.tk.ext); // Only liba/libs. // First figure out what we need to search for. // const string& name (*p.tk.name); // liba // path an; optional<string> ae; if (l || p.is_a<liba> ()) { // We are trying to find a library in the search paths extracted from // the compiler. It would only be natural if we used the library // prefix/extension that correspond to this compiler and/or its // target. // // Unlike MinGW, VC's .lib/.dll.lib naming is by no means standard and // we might need to search for other names. In fact, there is no // reliable way to guess from the file name what kind of library it // is, static or import and we will have to do deep inspection of such // alternative names. However, if we did find .dll.lib, then we can // assume that .lib is the static library without any deep inspection // overhead. // const char* e (""); if (tsys == "win32-msvc") { an = path (name); e = "lib"; } else { an = path ("lib" + name); e = "a"; } ae = ext ? ext : string (e); if (!ae->empty ()) { an += '.'; an += *ae; } } // libs // path sn; optional<string> se; if (l || p.is_a<libs> ()) { const char* e (""); if (tsys == "win32-msvc") { sn = path (name); e = "dll.lib"; } else { sn = path ("lib" + name); if (tsys == "darwin") e = "dylib"; else if (tsys == "mingw32") e = "dll.a"; // See search code below. else e = "so"; } se = ext ? ext : string (e); if (!se->empty ()) { sn += '.'; sn += *se; } } // Now search. // liba* a (nullptr); libs* s (nullptr); pair<path, path> pc; // pkg-config .pc file paths. path f; // Reuse the buffer. auto search =[&a, &s, &pc, &an, &ae, &sn, &se, &name, ext, &ld, &f, &p, exist, &trace, this] (const dir_path& d) -> bool { context& ctx (p.scope->ctx); timestamp mt; // libs // // Look for the shared library first. The order is important for VC: // only if we found .dll.lib can we safely assumy that just .lib is a // static library. // if (!sn.empty ()) { f = d; f /= sn; mt = mtime (f); if (mt != timestamp_nonexistent) { // On Windows what we found is the import library which we need // to make the first ad hoc member of libs{}. // if (tclass == "windows") { libi* i (nullptr); insert_library (ctx, i, name, d, ld, se, exist, trace); ulock l ( insert_library (ctx, s, name, d, ld, nullopt, exist, trace)); if (!exist) { if (l.owns_lock ()) { s->member = i; // We are first. l.unlock (); } else assert (find_adhoc_member<libi> (*s) == i); i->mtime (mt); i->path (move (f)); // Presumably there is a DLL somewhere, we just don't know // where (and its possible we might have to look for one if we // decide we need to do rpath emulation for installed // libraries as well). We will represent this as empty path // but valid timestamp (aka "trust me, it's there"). // s->mtime (mt); s->path (empty_path); } } else { insert_library (ctx, s, name, d, ld, se, exist, trace); s->mtime (mt); s->path (move (f)); } } else if (!ext && tsys == "mingw32") { // Above we searched for the import library (.dll.a) but if it's // not found, then we also search for the .dll (unless the // extension was specified explicitly) since we can link to it // directly. Note also that the resulting libs{} would end up // being the .dll. // se = string ("dll"); f = f.base (); // Remove .a from .dll.a. mt = mtime (f); if (mt != timestamp_nonexistent) { insert_library (ctx, s, name, d, ld, se, exist, trace); s->mtime (mt); s->path (move (f)); } } } // liba // // If we didn't find .dll.lib then we cannot assume .lib is static. // if (!an.empty () && (s != nullptr || tsys != "win32-msvc")) { f = d; f /= an; if ((mt = mtime (f)) != timestamp_nonexistent) { // Enter the target. Note that because the search paths are // normalized, the result is automatically normalized as well. // // Note that this target is outside any project which we treat // as out trees. // insert_library (ctx, a, name, d, ld, ae, exist, trace); a->mtime (mt); a->path (move (f)); } } // Alternative search for VC. // if (tsys == "win32-msvc") { if (s == nullptr && !sn.empty ()) s = msvc_search_shared (ld, d, p, exist); if (a == nullptr && !an.empty ()) a = msvc_search_static (ld, d, p, exist); } // Look for binary-less libraries via pkg-config .pc files. Note that // it is possible we have already found one of them as binfull but the // other is binless. // { bool na (a == nullptr && !an.empty ()); // Need static. bool ns (s == nullptr && !sn.empty ()); // Need shared. if (na || ns) { // Only consider the common .pc file if we can be sure there // is no binfull variant. // pair<path, path> r ( pkgconfig_search (d, p.proj, name, na && ns /* common */)); if (na && !r.first.empty ()) { insert_library (ctx, a, name, d, ld, nullopt, exist, trace); a->mtime (timestamp_unreal); a->path (empty_path); } if (ns && !r.second.empty ()) { insert_library (ctx, s, name, d, ld, nullopt, exist, trace); s->mtime (timestamp_unreal); s->path (empty_path); } // Only keep these .pc paths if we found anything via them. // if ((na && a != nullptr) || (ns && s != nullptr)) pc = move (r); } } return a != nullptr || s != nullptr; }; // First try user directories (i.e., -L or /LIBPATH). // bool sys (false); if (!usrd) { usrd = extract_library_search_dirs (*p.scope); // Handle automatic importing of installed build2 libraries. This is a // mirror side of the uninstalled case that is handled via the special // import.build2 value in import_search(). // if (build_installed && p.proj && *p.proj == "build2") { // Check if import.build2 is set to NULL to disable relying on the // built-in path. We use this in our tests to make sure we are // importing and testing the build system being built and not the // one doing the building. // // Note that for the installed case this value is undefined by // default. // lookup l (rs[ctx.var_import_build2]); if (!(l.defined () && l->null)) { // Note that we prepend it to other user directories instead of // making it the only one to allow things to be overriden (e.g., // if build2 was moved or some such). // usrd->insert (usrd->begin (), build_install_lib); } } } const dir_path* pd (nullptr); for (const dir_path& d: *usrd) { if (search (d)) { pd = &d; break; } } // Next try system directories (i.e., those extracted from the compiler). // if (pd == nullptr) { for (const dir_path& d: sysd) { if (search (d)) { pd = &d; break; } } sys = true; } if (pd == nullptr) return nullptr; // Enter (or find) the lib{} target group. // lib* lt; insert_library ( ctx, lt, name, *pd, ld, l ? p.tk.ext : nullopt, exist, trace); // Result. // target* r (l ? lt : (p.is_a<liba> () ? static_cast<target*> (a) : s)); // Assume the rest is already done if existing. // if (exist) return r; // If we cannot acquire the lock then this mean the target has already // been matched (though not clear by whom) and we assume all of this // has already been done. // target_lock ll (lock (act, *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. // if (ll) { if (a != nullptr) lt->a = a; if (s != nullptr) lt->s = s; } target_lock al (a != nullptr ? lock (act, *a) : target_lock ()); target_lock sl (s != nullptr ? lock (act, *s) : target_lock ()); if (!al) a = nullptr; if (!sl) s = nullptr; if (a != nullptr) a->group = lt; if (s != nullptr) s->group = lt; // Mark as a "cc" library (unless already marked) and set the system // flag. // auto mark_cc = [sys, this] (target& t) -> bool { auto p (t.vars.insert (c_type)); if (p.second) { p.first.get () = string ("cc"); if (sys) t.vars.assign (c_system) = true; } return p.second; }; // If the library already has cc.type, then assume it was either // already imported or was matched by a rule. // 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 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)); 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"); d += sanitize_identifier ( ucase (const_cast<const string&> (t.name))); d += '_'; d += suffix; strings o; o.push_back (move (d)); p.first.get () = move (o); } } }; if (ll && (a != nullptr || s != nullptr)) { // 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. // if (pc.first.empty () && pc.second.empty ()) { 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"); } } else pkgconfig_load (act, *p.scope, *lt, a, s, pc, *pd, sysd, *usrd); } // If we have the lock (meaning this is the first time), set the // traget's recipe to noop. Failed that we will keep re-locking it, // updating its members, etc. // if (al) match_recipe (al, noop_recipe); if (sl) match_recipe (sl, noop_recipe); if (ll) match_recipe (ll, noop_recipe); return r; } void gcc_extract_library_search_dirs (const strings&, dir_paths&); // gcc.cxx void msvc_extract_library_search_dirs (const strings&, dir_paths&); // msvc.cxx dir_paths common:: extract_library_search_dirs (const scope& bs) const { dir_paths r; // Extract user-supplied search paths (i.e., -L, /LIBPATH). // auto extract = [&bs, &r, this] (const value& val, const variable& var) { const auto& v (cast<strings> (val)); auto df = make_diag_frame ( [&var, &bs](const diag_record& dr) { dr << info << "in variable " << var << " for scope " << bs; }); if (tsys == "win32-msvc") msvc_extract_library_search_dirs (v, r); else gcc_extract_library_search_dirs (v, r); }; // Note that the compiler mode options are in sys_lib_dirs. // if (auto l = bs[c_loptions]) extract (*l, c_loptions); if (auto l = bs[x_loptions]) extract (*l, x_loptions); return r; } } }