aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc/common.cxx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-08-24 17:41:30 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-08-28 15:01:48 +0300
commit4bdf53837e010073de802070d4e6087410662d3e (patch)
tree2820d3964877d1a7d498833da325aa3d3a699353 /libbuild2/cc/common.cxx
parentea24f530048cbce0c5335ca3fd3632c8ce34315a (diff)
Move cc build system module to separate library
Diffstat (limited to 'libbuild2/cc/common.cxx')
-rw-r--r--libbuild2/cc/common.cxx1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/libbuild2/cc/common.cxx b/libbuild2/cc/common.cxx
new file mode 100644
index 0000000..bfcb00c
--- /dev/null
+++ b/libbuild2/cc/common.cxx
@@ -0,0 +1,1031 @@
+// file : libbuild2/cc/common.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// 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
+ // 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, true, true);
+ proc_opt (l, *t, false, true);
+ }
+ else
+ {
+ // For default export we use the same options as were used to
+ // build the library.
+ //
+ proc_opt (l, *t, true, false);
+ proc_opt (l, *t, false, false);
+ }
+ }
+ else
+ {
+ // Interface: only add *.export.* (interface dependencies).
+ //
+ proc_opt (l, *t, true, true);
+ proc_opt (l, *t, false, 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 (const name& n: *ns)
+ {
+ 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, *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,
+ name n,
+ linfo li,
+ const dir_paths& sysd,
+ optional<dir_paths>& usrd) const
+ {
+ if (n.type != "lib" && n.type != "liba" && n.type != "libs")
+ fail << "target name " << n << " is not a library";
+
+ const target* xt (nullptr);
+
+ if (!n.qualified ())
+ {
+ // Search for an existing target with this name "as if" it was a
+ // prerequisite.
+ //
+ xt = search_existing (n, s);
+
+ if (xt == nullptr)
+ fail << "unable to find library " << n;
+ }
+ else
+ {
+ // This is import.
+ //
+ 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 verifying that it already exists if requested. Return
+ // the lock.
+ //
+ template <typename T>
+ ulock common::
+ insert_library (context& ctx,
+ T*& r,
+ const string& name,
+ const dir_path& d,
+ optional<string> ext,
+ bool exist,
+ tracer& trace)
+ {
+ auto p (ctx.targets.insert_locked (T::static_type,
+ d,
+ dir_path (),
+ 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);
+
+ // @@ 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,
+ &p, &f, 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, se, exist, trace);
+
+ ulock l (
+ insert_library (ctx, s, name, d, 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, 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, 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, ae, exist, trace);
+ a->mtime (mt);
+ a->path (move (f));
+ }
+ }
+
+ // Alternative search for VC.
+ //
+ if (tsys == "win32-msvc")
+ {
+ const scope& rs (*p.scope->root_scope ());
+ const process_path& ld (cast<process_path> (rs["bin.ld.path"]));
+
+ 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, nullopt, exist, trace);
+ a->mtime (timestamp_unreal);
+ a->path (empty_path);
+ }
+
+ if (ns && !r.second.empty ())
+ {
+ insert_library (ctx, s, name, d, 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).
+ //
+ bool sys (false);
+
+ if (!usrd)
+ usrd = extract_library_dirs (*p.scope);
+
+ 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 (
+ p.scope->ctx, lt, name, *pd, 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;
+ }
+
+ dir_paths common::
+ extract_library_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));
+
+ for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
+ {
+ const string& o (*i);
+
+ dir_path d;
+
+ try
+ {
+ if (cclass == compiler_class::msvc)
+ {
+ // /LIBPATH:<dir> (case-insensitive).
+ //
+ if ((o[0] == '/' || o[0] == '-') &&
+ casecmp (o.c_str () + 1, "LIBPATH:", 8) == 0)
+ d = dir_path (o, 9, string::npos);
+ else
+ continue;
+ }
+ else
+ {
+ // -L can either be in the "-L<dir>" or "-L <dir>" form.
+ //
+ if (o == "-L")
+ {
+ if (++i == e)
+ break; // Let the compiler complain.
+
+ d = dir_path (*i);
+ }
+ else if (o.compare (0, 2, "-L") == 0)
+ d = dir_path (o, 2, string::npos);
+ else
+ continue;
+ }
+ }
+ catch (const invalid_path& e)
+ {
+ fail << "invalid directory '" << e.path << "'"
+ << " in option '" << o << "'"
+ << " in variable " << var
+ << " for scope " << bs;
+ }
+
+ // Ignore relative paths. Or maybe we should warn?
+ //
+ if (!d.relative ())
+ r.push_back (move (d));
+ }
+ };
+
+ if (auto l = bs[c_loptions]) extract (*l, c_loptions);
+ if (auto l = bs[x_loptions]) extract (*l, x_loptions);
+
+ return r;
+ }
+ }
+}