aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc/functions.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/cc/functions.cxx')
-rw-r--r--libbuild2/cc/functions.cxx154
1 files changed, 118 insertions, 36 deletions
diff --git a/libbuild2/cc/functions.cxx b/libbuild2/cc/functions.cxx
index cafb7f0..9d408af 100644
--- a/libbuild2/cc/functions.cxx
+++ b/libbuild2/cc/functions.cxx
@@ -13,11 +13,10 @@
#include <libbuild2/cc/module.hxx>
#include <libbuild2/cc/utility.hxx>
+#include <libbuild2/functions-name.hxx> // to_target()
+
namespace build2
{
- const target&
- to_target (const scope&, name&&, name&&); // libbuild2/functions-name.cxx
-
namespace cc
{
using namespace bin;
@@ -47,8 +46,13 @@ namespace build2
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";
+ // Note that we also allow calling this during match since an ad hoc
+ // recipe with dynamic dependency extraction (depdb-dyndep) executes its
+ // depdb preamble during match (after matching all the prerequisites).
+ //
+ if (bs->ctx.phase != run_phase::match &&
+ bs->ctx.phase != run_phase::execute)
+ fail << f.name << " can only be called from recipe";
const module* m (rs->find_module<module> (d.x));
@@ -57,6 +61,9 @@ namespace build2
// We can assume these are present due to function's types signature.
//
+ if (vs[0].null)
+ throw invalid_argument ("null value");
+
names& ts_ns (vs[0].as<names> ()); // <targets>
// In a somewhat hackish way strip the outer operation to match how we
@@ -70,20 +77,40 @@ namespace build2
{
name& n (*i), o;
const target& t (to_target (*bs, move (n), move (n.pair ? *++i : o)));
+
+ if (!t.matched (a))
+ fail << t << " is not matched" <<
+ info << "make sure this target is listed as prerequisite";
+
d.f (r, vs, *m, *bs, a, t);
}
return value (move (r));
}
- // Common thunk for $x.lib_*(<targets>, <otype> [, ...]) functions.
+ // Common thunk for $x.lib_*(...) functions.
+ //
+ // The two supported function signatures are:
+ //
+ // $x.lib_*(<targets>, <otype> [, ...]])
+ //
+ // $x.lib_*(<targets>)
+ //
+ // For the first signature, the passed targets cannot be library groups
+ // (so they are always file-based) and linfo is always present.
+ //
+ // For the second signature, targets can only be utility libraries
+ // (including the libul{} group).
+ //
+ // If <otype> in the first signature is NULL, then it is treated as
+ // the second signature.
//
struct lib_thunk_data
{
const char* x;
void (*f) (void*, strings&,
const vector_view<value>&, const module&, const scope&,
- action, const file&, bool, linfo);
+ action, const target&, bool, optional<linfo>);
};
static value
@@ -102,21 +129,27 @@ namespace build2
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";
+ if (bs->ctx.phase != run_phase::match && // See above.
+ bs->ctx.phase != run_phase::execute)
+ fail << f.name << " can only be called from recipe";
const module* m (rs->find_module<module> (d.x));
if (m == nullptr)
fail << f.name << " called without " << d.x << " module loaded";
- // We can assume these are present due to function's types signature.
+ // We can assume this is present due to function's types signature.
//
+ if (vs[0].null)
+ throw invalid_argument ("null value");
+
names& ts_ns (vs[0].as<names> ()); // <targets>
- names& ot_ns (vs[1].as<names> ()); // <otype>
- linfo li;
+ optional<linfo> li;
+ if (vs.size () > 1 && !vs[1].null)
{
+ names& ot_ns (vs[1].as<names> ()); // <otype>
+
string t (convert<string> (move (ot_ns)));
const target_type* tt (bs->find_target_type (t));
@@ -162,17 +195,22 @@ namespace build2
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> ())))
+ if (li
+ ? ((la = t.is_a<libux> ()) ||
+ (la = t.is_a<liba> ()) ||
+ ( t.is_a<libs> ()))
+ : ((la = t.is_a<libux> ()) ||
+ ( t.is_a<libul> ())))
{
- d.f (ls, r, vs, *m, *bs, a, *f, la, li);
+ if (!t.matched (a))
+ fail << t << " is not matched" <<
+ info << "make sure this target is listed as prerequisite";
+
+ d.f (ls, r, vs, *m, *bs, a, t, la, li);
}
else
- fail << t << " is not a library target";
+ fail << t << " is not a library of expected type";
}
return value (move (r));
@@ -199,33 +237,61 @@ namespace build2
void compile_rule::
functions (function_family& f, const char* x)
{
- // $<module>.lib_poptions(<lib-targets>, <otype>)
+ // $<module>.lib_poptions(<lib-targets>[, <otype>[, <original>]])
//
// 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).
//
+ // The output target type may be omitted for utility libraries (libul{}
+ // or libu[eas]{}). In this case, only "common interface" options will
+ // be returned for lib{} dependencies. This is primarily useful for
+ // obtaining poptions to be passed to tools other than C/C++ compilers
+ // (for example, Qt moc).
+ //
+ // If <original> is true, then return the original -I options without
+ // performing any translation (for example, to -isystem or /external:I).
+ // This is the default if <otype> is omitted. To get the translation for
+ // the common interface options, pass [null] for <otype> and true for
+ // <original>.
+ //
// Note that passing multiple targets at once is not a mere convenience:
// this also allows for more effective duplicate suppression.
//
- // Note also 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.
+ // Note also that this function can only be called during execution (or,
+ // carefully, during match) after all the specified library targets have
+ // been matched. Normally it is used in ad hoc recipes to implement
+ // custom compilation.
//
// Note that this function is not pure.
//
f.insert (".lib_poptions", false).
- insert<lib_thunk_data, names, names> (
+ insert<lib_thunk_data, names, optional<names*>, optional<names>> (
&lib_thunk<appended_libraries>,
lib_thunk_data {
x,
[] (void* ls, strings& r,
- const vector_view<value>&, const module& m, const scope& bs,
- action a, const file& l, bool la, linfo li)
+ const vector_view<value>& vs, const module& m, const scope& bs,
+ action a, const target& l, bool la, optional<linfo> li)
{
+ // If this is libul{}, get the matched member (see bin::libul_rule
+ // for details).
+ //
+ const file& f (
+ la || li
+ ? l.as<file> ()
+ : (la = true,
+ l.prerequisite_targets[a].back ().target->as<file> ()));
+
+ bool common (!li);
+ bool original (vs.size () > 2 ? convert<bool> (vs[2]) : !li);
+
+ if (!li)
+ li = link_info (bs, link_type (f).type);
+
m.append_library_options (
*static_cast<appended_libraries*> (ls), r,
- bs, a, l, la, li);
+ bs, a, f, la, *li, common, original);
}});
// $<module>.find_system_header(<name>)
@@ -289,9 +355,10 @@ namespace build2
// Note that passing multiple targets at once is not a mere convenience:
// this also allows for more effective duplicate suppression.
//
- // Note also 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.
+ // Note also that this function can only be called during execution (or,
+ // carefully, during match) after all the specified library targets have
+ // been matched. Normally it is used in ad hoc recipes to implement
+ // custom linking.
//
// Note that this function is not pure.
//
@@ -302,12 +369,15 @@ namespace build2
x,
[] (void* ls, strings& r,
const vector_view<value>& vs, const module& m, const scope& bs,
- action a, const file& l, bool la, linfo li)
+ action a, const target& l, bool la, optional<linfo> li)
{
lflags lf (0);
bool rel (true);
if (vs.size () > 2)
{
+ if (vs[2].null)
+ throw invalid_argument ("null value");
+
for (const name& f: vs[2].as<names> ())
{
string s (convert<string> (name (f)));
@@ -326,7 +396,8 @@ namespace build2
m.append_libraries (
*static_cast<appended_libraries*> (ls), r,
nullptr /* sha256 */, nullptr /* update */, timestamp_unknown,
- bs, a, l, la, lf, li, nullopt /* for_install */, self, rel);
+ bs, a, l.as<file> (), la, lf, *li,
+ nullopt /* for_install */, self, rel);
}});
// $<module>.lib_rpaths(<lib-targets>, <otype> [, <link> [, <self>]])
@@ -358,13 +429,12 @@ namespace build2
x,
[] (void* ls, strings& r,
const vector_view<value>& vs, const module& m, const scope& bs,
- action a, const file& l, bool la, linfo li)
+ action a, const target& l, bool la, optional<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 (*static_cast<rpathed_libraries*> (ls), r,
- bs,
- a, l, la, li, link, self);
+ bs, a, l.as<file> (), la, *li, link, self);
}});
// $cxx.obj_modules(<obj-targets>)
@@ -422,7 +492,16 @@ namespace build2
// look for cc.export.libs and <module>.export.libs.
//
// 3. No member/group selection/linkup: we resolve *.export.libs on
- // whatever is listed.
+ // whatever is listed (so no liba{}/libs{} overrides will be
+ // considered).
+ //
+ // Because of (2) and (3), this functionality should only be used on a
+ // controlled list of libraries (usually libraries that belong to the
+ // same family as this library).
+ //
+ // Note that a similar deduplication is also performed when processing
+ // the libraries. However, it may still make sense to do it once at the
+ // source for really severe cases (like Boost).
//
// Note that this function is not pure.
//
@@ -450,6 +529,9 @@ namespace build2
// We can assume the argument is present due to function's types
// signature.
//
+ if (vs[0].null)
+ throw invalid_argument ("null value");
+
names& r (vs[0].as<names> ());
m->deduplicate_export_libs (*bs,
vector<name> (r.begin (), r.end ()),