From 4bdf53837e010073de802070d4e6087410662d3e Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 24 Aug 2019 17:41:30 +0300 Subject: Move cc build system module to separate library --- libbuild2/cc/common.cxx | 1031 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1031 insertions(+) create mode 100644 libbuild2/cc/common.cxx (limited to 'libbuild2/cc/common.cxx') 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 + +#include // import() +#include +#include +#include +#include +#include + +#include + +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& proc_impl, // Implementation? + const function& proc_lib, // True if system library. + const function& proc_opt, // *.export. + bool self /*= false*/, // Call proc_lib on l? + small_vector* chain) const + { + small_vector 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 (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 (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 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 ( + 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 ())) || + (la = (f = pt->is_a ())) || + ( f = pt->is_a ())) + { + 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 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* ns (cast_null> (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 () || t.is_a (), 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 (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& 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& 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 ()) + xt = link_member (*l, a, li); // Pick lib*{e,a,s}{}. + + return xt->as (); + } + + // Insert a target verifying that it already exists if requested. Return + // the lock. + // + template + ulock common:: + insert_library (context& ctx, + T*& r, + const string& name, + const dir_path& d, + optional 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 (); + 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& 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 ()); + const optional& 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 ae; + + if (l || p.is_a ()) + { + // 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 se; + + if (l || p.is_a ()) + { + 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 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 (*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 (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 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 () ? static_cast (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_{STATIC,SHARED}, + // where 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 (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 (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: (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" or "-L " 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; + } + } +} -- cgit v1.1