diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2020-11-11 15:14:19 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2020-11-11 15:14:19 +0200 |
commit | cd10a583ad1f3c299383c07fd8c6ccd6e3199e6b (patch) | |
tree | acf33fe1a9c03fadf8e96ea1b78a89ff7de95372 /libbuild2/cc/functions.cxx | |
parent | 60a95915aa1fda93d308158416071ba39286d41c (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/functions.cxx')
-rw-r--r-- | libbuild2/cc/functions.cxx | 231 |
1 files changed, 231 insertions, 0 deletions
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); + }}); + } + } +} |