aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-11-11 15:14:19 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2020-11-11 15:14:19 +0200
commitcd10a583ad1f3c299383c07fd8c6ccd6e3199e6b (patch)
treeacf33fe1a9c03fadf8e96ea1b78a89ff7de95372 /libbuild2/cc
parent60a95915aa1fda93d308158416071ba39286d41c (diff)
Add ${c,cxx}.lib_{poptions,libs,rpaths}() functions
These functions can be used to query library metadata for options and libraries that should be used when compiling/linking dependent targets, similar to how cc::{compile,link}_rule do it. With this support it should be possible to more or less re-create their semantics in ad hoc recipes.
Diffstat (limited to 'libbuild2/cc')
-rw-r--r--libbuild2/cc/compile-rule.cxx59
-rw-r--r--libbuild2/cc/compile-rule.hxx33
-rw-r--r--libbuild2/cc/functions.cxx231
-rw-r--r--libbuild2/cc/link-rule.cxx72
-rw-r--r--libbuild2/cc/link-rule.hxx46
-rw-r--r--libbuild2/cc/module.cxx18
-rw-r--r--libbuild2/cc/module.hxx8
-rw-r--r--libbuild2/cc/utility.hxx13
-rw-r--r--libbuild2/cc/utility.ixx12
9 files changed, 388 insertions, 104 deletions
diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx
index 832ed9b..6ddff08 100644
--- a/libbuild2/cc/compile-rule.cxx
+++ b/libbuild2/cc/compile-rule.cxx
@@ -380,11 +380,9 @@ namespace build2
//
template <typename T>
void compile_rule::
- append_lib_options (const scope& bs,
- T& args,
- action a,
- const target& t,
- linfo li) const
+ append_lib_options (T& args,
+ const scope& bs,
+ action a, const file& l, bool la, linfo li) const
{
// See through utility libraries.
//
@@ -409,11 +407,25 @@ namespace build2
append_options (args, l, var);
};
- // In case we don't have the "small function object" optimization.
- //
- const function<bool (const file&, bool)> impf (imp);
- const function<void (const file&, const string&, bool, bool)> optf (opt);
+ process_libraries (a, bs, li, sys_lib_dirs,
+ l, la, 0, // Hack: lflags unused.
+ imp, nullptr, opt);
+ }
+ void compile_rule::
+ append_lib_options (strings& args,
+ const scope& bs,
+ action a, const file& l, bool la, linfo li) const
+ {
+ append_lib_options<strings> (args, bs, a, l, la, li);
+ }
+
+ template <typename T>
+ void compile_rule::
+ append_lib_options (T& args,
+ const scope& bs,
+ action a, const target& t, linfo li) const
+ {
for (prerequisite_member p: group_prerequisite_members (a, t))
{
if (include (a, t, p) != include_type::normal) // Excluded/ad hoc.
@@ -427,14 +439,13 @@ namespace build2
pt = link_member (*l, a, li);
bool la;
- if (!((la = pt->is_a<liba> ()) ||
- (la = pt->is_a<libux> ()) ||
- pt->is_a<libs> ()))
- continue;
-
- process_libraries (a, bs, li, sys_lib_dirs,
- pt->as<file> (), la, 0, // Hack: lflags unused.
- impf, nullptr, optf);
+ const file* f;
+ if ((la = (f = pt->is_a<liba> ())) ||
+ (la = (f = pt->is_a<libux> ())) ||
+ ( (f = pt->is_a<libs> ())))
+ {
+ append_lib_options (args, bs, a, *f, la, li);
+ }
}
}
}
@@ -443,8 +454,8 @@ namespace build2
// recursively, prerequisite libraries first.
//
void compile_rule::
- append_lib_prefixes (const scope& bs,
- prefix_map& m,
+ append_lib_prefixes (prefix_map& m,
+ const scope& bs,
action a,
target& t,
linfo li) const
@@ -888,7 +899,7 @@ namespace build2
// Hash *.export.poptions from prerequisite libraries.
//
- append_lib_options (bs, cs, a, t, li);
+ append_lib_options (cs, bs, a, t, li);
}
append_options (cs, t, c_coptions);
@@ -1474,7 +1485,7 @@ namespace build2
// Then process the include directories from prerequisite libraries.
//
- append_lib_prefixes (bs, m, a, t, li);
+ append_lib_prefixes (m, bs, a, t, li);
return m;
}
@@ -2906,7 +2917,7 @@ namespace build2
// Add *.export.poptions from prerequisite libraries.
//
- append_lib_options (bs, args, a, t, li);
+ append_lib_options (args, bs, a, t, li);
// Populate the src-out with the -I$out_base -I$src_base pairs.
//
@@ -4302,7 +4313,7 @@ namespace build2
append_options (args, t, x_poptions);
append_options (args, t, c_poptions);
- append_lib_options (t.base_scope (), args, a, t, li);
+ append_lib_options (args, t.base_scope (), a, t, li);
if (md.symexport)
append_symexport_options (args, t);
@@ -5929,7 +5940,7 @@ namespace build2
// Add *.export.poptions from prerequisite libraries.
//
- append_lib_options (bs, args, a, t, li);
+ append_lib_options (args, bs, a, t, li);
if (md.symexport)
append_symexport_options (args, t);
diff --git a/libbuild2/cc/compile-rule.hxx b/libbuild2/cc/compile-rule.hxx
index d6cb002..cbbb142 100644
--- a/libbuild2/cc/compile-rule.hxx
+++ b/libbuild2/cc/compile-rule.hxx
@@ -53,6 +53,15 @@ namespace build2
target_state
perform_clean (action, const target&) const;
+ public:
+ void
+ append_lib_options (strings&,
+ const scope&,
+ action, const file&, bool, linfo) const;
+ protected:
+ static void
+ functions (function_family&, const char*); // functions.cxx
+
private:
struct match_data;
using environment = small_vector<const char*, 2>;
@@ -63,11 +72,15 @@ namespace build2
template <typename T>
void
- append_lib_options (const scope&,
- T&,
- action,
- const target&,
- linfo) const;
+ append_lib_options (T&,
+ const scope&,
+ action, const file&, bool, linfo) const;
+
+ template <typename T>
+ void
+ append_lib_options (T&,
+ const scope&,
+ action, const target&, linfo) const;
// Mapping of include prefixes (e.g., foo in <foo/bar>) for auto-
// generated headers to directories where they will be generated.
@@ -94,11 +107,9 @@ namespace build2
append_prefixes (prefix_map&, const target&, const variable&) const;
void
- append_lib_prefixes (const scope&,
- prefix_map&,
- action,
- target&,
- linfo) const;
+ append_lib_prefixes (prefix_map&,
+ const scope&,
+ action, target&, linfo) const;
prefix_map
build_prefix_map (const scope&, action, target&, linfo) const;
@@ -168,7 +179,7 @@ namespace build2
action, const file&,
const match_data&, const path&) const;
- // Compiler-specific language selection option. Return the number of
+ // Compiler-specific language selection options. Return the number of
// options (arguments, really) appended.
//
size_t
diff --git a/libbuild2/cc/functions.cxx b/libbuild2/cc/functions.cxx
new file mode 100644
index 0000000..ca93b63
--- /dev/null
+++ b/libbuild2/cc/functions.cxx
@@ -0,0 +1,231 @@
+// file : libbuild2/cc/functions.cxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/cc/link-rule.hxx>
+#include <libbuild2/cc/compile-rule.hxx>
+
+#include <libbuild2/function.hxx>
+#include <libbuild2/variable.hxx>
+
+#include <libbuild2/bin/target.hxx>
+#include <libbuild2/bin/utility.hxx>
+
+#include <libbuild2/cc/module.hxx>
+#include <libbuild2/cc/utility.hxx>
+
+namespace build2
+{
+ const target&
+ to_target (const scope&, name&&, name&&); // libbuild2/functions-name.cxx
+
+ namespace cc
+ {
+ using namespace bin;
+
+ // Common thunk for $x.lib_*(<targets>, <otype> [, ...]) functions.
+ //
+ struct lib_data
+ {
+ const char* x;
+ void (*f) (strings&,
+ const vector_view<value>&, const module&, const scope&,
+ action, const file&, bool, linfo);
+ };
+
+ static value
+ lib_thunk (const scope* bs,
+ vector_view<value> vs,
+ const function_overload& f)
+ {
+ const lib_data& d (*reinterpret_cast<const lib_data*> (&f.data));
+
+ if (bs == nullptr)
+ fail << f.name << " called out of scope";
+
+ const scope* rs (bs->root_scope ());
+
+ if (rs == nullptr)
+ fail << f.name << " called out of project";
+
+ if (bs->ctx.phase != run_phase::execute)
+ fail << f.name << " can only be called during execution";
+
+ const module* m (rs->find_module<module> (d.x));
+
+ if (m == nullptr)
+ fail << f.name << " called without " << d.x << " module being loaded";
+
+ // We can assume these are present due to function's types signature.
+ //
+ names& ts_ns (vs[0].as<names> ()); // <targets>
+ names& ot_ns (vs[1].as<names> ()); // <otype>
+
+ linfo li;
+ {
+ string t (convert<string> (move (ot_ns)));
+
+ const target_type* tt (bs->find_target_type (t));
+
+ if (tt == nullptr)
+ fail << "unknown target type '" << t << "'";
+
+ // Try both linker and compiler output types.
+ //
+ otype ot (link_type (*tt).type);
+
+ switch (ot)
+ {
+ case otype::e:
+ case otype::a:
+ case otype::s:
+ break;
+ default:
+ ot = compile_type (*tt);
+ switch (ot)
+ {
+ case otype::e:
+ case otype::a:
+ case otype::s:
+ break;
+ default:
+ fail << "target type " << t << " is not compiler/linker output";
+ }
+ }
+
+ li = link_info (*bs, ot);
+ }
+
+ // In a somewhat hackish way strip the outer operation to match how we
+ // call the underlying functions in the compile/link rules. This should
+ // be harmless since ad hoc recipes are always for the inner operation.
+ //
+ action a (rs->ctx.current_action ().inner_action ());
+
+ strings r;
+ for (auto i (ts_ns.begin ()); i != ts_ns.end (); ++i)
+ {
+ name& n (*i), o;
+ const target& t (to_target (*bs, move (n), move (n.pair ? *++i : o)));
+
+ const file* f;
+ bool la (false);
+
+ if ((la = (f = t.is_a<libux> ())) ||
+ (la = (f = t.is_a<liba> ())) ||
+ ( (f = t.is_a<libs> ())))
+ {
+ d.f (r, vs, *m, *bs, a, *f, la, li);
+ }
+ else
+ fail << t << " is not a library target";
+ }
+
+ return value (move (r));
+ }
+
+ void compile_rule::
+ functions (function_family& f, const char* x)
+ {
+ // $<module>.lib_poptions(<targets>, <otype>)
+ //
+ // Return the preprocessor options that should be passed when compiling
+ // sources that depend on the specified libraries. The second argument
+ // is the output target type (obje, objs, etc).
+ //
+ // Note that this function can only be called during execution after all
+ // the specified library targets have been matched. Normally it is used
+ // in ad hoc recipes to implement custom compilation.
+ //
+ f[".lib_poptions"].insert<lib_data, names, names> (
+ &lib_thunk,
+ lib_data {
+ x,
+ [] (strings& r,
+ const vector_view<value>&, const module& m, const scope& bs,
+ action a, const file& l, bool la, linfo li)
+ {
+ m.append_lib_options (r, bs, a, l, la, li);
+ }});
+ }
+
+ void link_rule::
+ functions (function_family& f, const char* x)
+ {
+ // $<module>.lib_libs(<targets>, <otype> [, <flags> [, <self>]])
+ //
+ // Return the libraries (and any associated options) that should be
+ // passed when linking targets that depend on the specified libraries.
+ // The second argument is the output target type (exe, libs, etc).
+ //
+ // The following flags are supported:
+ //
+ // whole - link the specified libraries in the whole archive mode
+ //
+ // If the last argument is false, then do not return the specified
+ // libraries themselves.
+ //
+ // Note that this function can only be called during execution after all
+ // the specified library targets have been matched. Normally it is used
+ // in ad hoc recipes to implement custom linking.
+ //
+ f[".lib_libs"].insert<lib_data,
+ names, names, optional<names>, optional<names>> (
+ &lib_thunk,
+ lib_data {
+ x,
+ [] (strings& r,
+ const vector_view<value>& vs, const module& m, const scope& bs,
+ action a, const file& l, bool la, linfo li)
+ {
+ lflags lf (0);
+ if (vs.size () > 2)
+ {
+ for (const name& f: vs[2].as<names> ())
+ {
+ string s (convert<string> (name (f)));
+
+ if (s == "whole")
+ lf |= lflag_whole;
+ else
+ fail << "invalid flag '" << s << "'";
+ }
+ }
+
+ bool self (vs.size () > 3 ? convert<bool> (vs[3]) : true);
+
+ m.append_libraries (r, bs, a, l, la, lf, li, self);
+ }});
+
+ // $<module>.lib_rpaths(<targets>, <otype> [, <link> [, <self>]])
+ //
+ // Return the rpath options that should be passed when linking targets
+ // that depend on the specified libraries. The second argument is the
+ // output target type (exe, libs, etc).
+ //
+ // If the third argument is true, then use rpath-link options rather
+ // than rpath (which is what should normally be used when linking for
+ // install, for example).
+ //
+ // If the last argument is false, then do not return the options for the
+ // specified libraries themselves.
+ //
+ // Note that this function can only be called during execution after all
+ // the specified library targets have been matched. Normally it is used
+ // in ad hoc recipes to implement custom linking.
+ //
+ f[".lib_rpaths"].insert<lib_data,
+ names, names, optional<names>, optional<names>> (
+ &lib_thunk,
+ lib_data {
+ x,
+ [] (strings& r,
+ const vector_view<value>& vs, const module& m, const scope& bs,
+ action a, const file& l, bool la, linfo li)
+ {
+ bool link (vs.size () > 2 ? convert<bool> (vs[2]) : false);
+ bool self (vs.size () > 3 ? convert<bool> (vs[3]) : true);
+ m.rpath_libraries (r, bs, a, l, la, li, link, self);
+ }});
+ }
+ }
+}
diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx
index 6acb1c7..b22cae0 100644
--- a/libbuild2/cc/link-rule.cxx
+++ b/libbuild2/cc/link-rule.cxx
@@ -1490,8 +1490,9 @@ namespace build2
void link_rule::
append_libraries (strings& args,
- const file& l, bool la, lflags lf,
- const scope& bs, action a, linfo li) const
+ const scope& bs, action a,
+ const file& l, bool la, lflags lf, linfo li,
+ bool self) const
{
struct data
{
@@ -1664,14 +1665,13 @@ namespace build2
};
process_libraries (
- a, bs, li, sys_lib_dirs, l, la, lf, imp, lib, opt, true);
+ a, bs, li, sys_lib_dirs, l, la, lf, imp, lib, opt, self);
}
void link_rule::
- append_libraries (sha256& cs,
- bool& update, timestamp mt,
- const file& l, bool la, lflags lf,
- const scope& bs, action a, linfo li) const
+ append_libraries (sha256& cs, bool& update, timestamp mt,
+ const scope& bs, action a,
+ const file& l, bool la, lflags lf, linfo li) const
{
struct data
{
@@ -1771,11 +1771,9 @@ namespace build2
void link_rule::
rpath_libraries (strings& args,
- const target& t,
const scope& bs,
- action a,
- linfo li,
- bool link) const
+ action a, const file& l, bool la,
+ linfo li, bool link, bool self) const
{
// Use -rpath-link only on targets that support it (Linux, *BSD). Note
// that we don't really need it for top-level libraries.
@@ -1874,12 +1872,30 @@ namespace build2
d.args.push_back (move (o));
};
- // In case we don't have the "small function object" optimization.
- //
- const function<bool (const file&, bool)> impf (imp);
- const function<
- void (const file* const*, const string&, lflags, bool)> libf (lib);
+ if (self && !link && !la)
+ {
+ // Top-level shared library dependency.
+ //
+ if (!l.path ().empty ()) // Not binless.
+ {
+ // It is either matched or imported so should be a cc library.
+ //
+ if (!cast_false<bool> (l.vars[c_system]))
+ args.push_back ("-Wl,-rpath," + l.path ().directory ().string ());
+ }
+ }
+
+ process_libraries (a, bs, li, sys_lib_dirs,
+ l, la, 0 /* lflags */,
+ imp, lib, nullptr);
+ }
+
+ void link_rule::
+ rpath_libraries (strings& args,
+ const scope& bs, action a,
+ const target& t, linfo li, bool link) const
+ {
for (const prerequisite_target& pt: t.prerequisite_targets[a])
{
if (pt == nullptr)
@@ -1892,23 +1908,7 @@ namespace build2
(la = (f = pt->is_a<libux> ())) ||
( f = pt->is_a<libs> ()))
{
- if (!link && !la)
- {
- // Top-level shared library dependency.
- //
- if (!f->path ().empty ()) // Not binless.
- {
- // It is either matched or imported so should be a cc library.
- //
- if (!cast_false<bool> (f->vars[c_system]))
- args.push_back (
- "-Wl,-rpath," + f->path ().directory ().string ());
- }
- }
-
- process_libraries (a, bs, li, sys_lib_dirs,
- *f, la, pt.data,
- impf, libf, nullptr);
+ rpath_libraries (args, bs, a, *f, la, li, link, true);
}
}
}
@@ -2431,7 +2431,7 @@ namespace build2
if (cast_true<bool> (t[for_install
? "bin.rpath_link.auto"
: "bin.rpath.auto"]))
- rpath_libraries (sargs, t, bs, a, li, for_install /* link */);
+ rpath_libraries (sargs, bs, a, t, li, for_install /* link */);
lookup l;
@@ -2575,7 +2575,7 @@ namespace build2
//
if (la || ls)
{
- append_libraries (cs, update, mt, *f, la, p.data, bs, a, li);
+ append_libraries (cs, update, mt, bs, a, *f, la, p.data, li);
f = nullptr; // Timestamp checked by hash_libraries().
}
else
@@ -2886,7 +2886,7 @@ namespace build2
(ls = (f = pt->is_a<libs> ())))))
{
if (la || ls)
- append_libraries (sargs, *f, la, p.data, bs, a, li);
+ append_libraries (sargs, bs, a, *f, la, p.data, li);
else
{
sargs.push_back (relative (f->path ()).string ()); // string()&&
diff --git a/libbuild2/cc/link-rule.hxx b/libbuild2/cc/link-rule.hxx
index db48160..a93defc 100644
--- a/libbuild2/cc/link-rule.hxx
+++ b/libbuild2/cc/link-rule.hxx
@@ -51,6 +51,33 @@ namespace build2
using simple_rule::match; // To make Clang happy.
+ public:
+ // Library handling.
+ //
+ void
+ append_libraries (strings&,
+ const scope&, action,
+ const file&, bool, lflags, linfo, bool = true) const;
+
+ void
+ append_libraries (sha256&, bool&, timestamp,
+ const scope&, action,
+ const file&, bool, lflags, linfo) const;
+
+ void
+ rpath_libraries (strings&,
+ const scope&,
+ action, const file&, bool, linfo, bool, bool) const;
+
+ void
+ rpath_libraries (strings&,
+ const scope&, action,
+ const target&, linfo, bool) const;
+
+ protected:
+ static void
+ functions (function_family&, const char*); // functions.cxx
+
private:
friend class install_rule;
friend class libux_install_rule;
@@ -126,25 +153,6 @@ namespace build2
link_rule::libs_paths libs_paths;
};
- // Library handling.
- //
- void
- append_libraries (strings&,
- const file&, bool, lflags,
- const scope&, action, linfo) const;
-
- void
- append_libraries (sha256&,
- bool&, timestamp,
- const file&, bool, lflags,
- const scope&, action, linfo) const;
-
- void
- rpath_libraries (strings&,
- const target&,
- const scope&, action, linfo,
- bool) const;
-
// Windows rpath emulation (windows-rpath.cxx).
//
struct windows_dll
diff --git a/libbuild2/cc/module.cxx b/libbuild2/cc/module.cxx
index 4b4f5e2..14182a2 100644
--- a/libbuild2/cc/module.cxx
+++ b/libbuild2/cc/module.cxx
@@ -6,6 +6,7 @@
#include <iomanip> // left, setw()
#include <libbuild2/scope.hxx>
+#include <libbuild2/function.hxx>
#include <libbuild2/diagnostics.hxx>
#include <libbuild2/bin/target.hxx>
@@ -677,6 +678,18 @@ namespace build2
{
tracer trace (x, "init");
+ context& ctx (rs.ctx);
+
+ // Register the module function family if this is the first instance of
+ // this modules.
+ //
+ if (!function_family::defined (ctx.functions, x))
+ {
+ function_family f (ctx.functions, x);
+ compile_rule::functions (f, x);
+ link_rule::functions (f, x);
+ }
+
// Load cc.core. Besides other things, this will load bin (core) plus
// extra bin.* modules we may need.
//
@@ -798,13 +811,12 @@ namespace build2
bool s (tsys != "emscripten");
auto& r (rs.rules);
+ const compile_rule& cr (*this);
+ const link_rule& lr (*this);
// We register for configure so that we detect unresolved imports
// during configuration rather that later, e.g., during update.
//
- const compile_rule& cr (*this);
- const link_rule& lr (*this);
-
r.insert<obje> (perform_update_id, x_compile, cr);
r.insert<obje> (perform_clean_id, x_compile, cr);
r.insert<obje> (configure_update_id, x_compile, cr);
diff --git a/libbuild2/cc/module.hxx b/libbuild2/cc/module.hxx
index 81456b3..85b7158 100644
--- a/libbuild2/cc/module.hxx
+++ b/libbuild2/cc/module.hxx
@@ -88,10 +88,10 @@ namespace build2
class LIBBUILD2_CC_SYMEXPORT module: public build2::module,
public virtual common,
- link_rule,
- compile_rule,
- install_rule,
- libux_install_rule
+ public link_rule,
+ public compile_rule,
+ public install_rule,
+ public libux_install_rule
{
public:
explicit
diff --git a/libbuild2/cc/utility.hxx b/libbuild2/cc/utility.hxx
index 458aa25..a856fd0 100644
--- a/libbuild2/cc/utility.hxx
+++ b/libbuild2/cc/utility.hxx
@@ -32,10 +32,19 @@ namespace build2
extern const dir_path module_build_dir; // cc/build/
extern const dir_path module_build_modules_dir; // cc/build/modules/
- // Compile output type from source target.
+ // Compile output type from output target type (obj*{}, bmi*{}, etc).
+ //
+ // If input unit type is specified, then restrict the tests only to output
+ // types that can be produced from this input.
//
otype
- compile_type (const target&, unit_type);
+ compile_type (const target_type&, optional<unit_type> = nullopt);
+
+ inline otype
+ compile_type (const target& t, optional<unit_type> ut = nullopt)
+ {
+ return compile_type (t.type (), ut);
+ }
compile_target_types
compile_types (otype);
diff --git a/libbuild2/cc/utility.ixx b/libbuild2/cc/utility.ixx
index 0b94780..46a4d0d 100644
--- a/libbuild2/cc/utility.ixx
+++ b/libbuild2/cc/utility.ixx
@@ -6,21 +6,23 @@ namespace build2
namespace cc
{
inline otype
- compile_type (const target& t, unit_type u)
+ compile_type (const target_type& t, optional<unit_type> u)
{
using namespace bin;
auto test = [&t, u] (const auto& h, const auto& i, const auto& o)
{
- return t.is_a (u == unit_type::module_header ? h :
- u == unit_type::module_iface ? i :
- o);
+ return (u
+ ? t.is_a (*u == unit_type::module_header ? h :
+ *u == unit_type::module_iface ? i : o)
+ : t.is_a (h) || t.is_a (i) || t.is_a (o));
};
return
test (hbmie::static_type, bmie::static_type, obje::static_type) ? otype::e :
+ test (hbmis::static_type, bmis::static_type, objs::static_type) ? otype::s :
test (hbmia::static_type, bmia::static_type, obja::static_type) ? otype::a :
- otype::s;
+ static_cast<otype> (0xFF);
}
inline compile_target_types