// file : libbuild2/cc/functions.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include #include #include #include #include #include #include #include namespace build2 { const target& to_target (const scope&, name&&, name&&); // libbuild2/functions-name.cxx namespace cc { using namespace bin; // Common thunk for $x.*( [, ...]) functions. // struct thunk_data { const char* x; void (*f) (strings&, const vector_view&, const module&, const scope&, action, const target&); }; static value thunk (const scope* bs, vector_view vs, const function_overload& f) { const auto& d (*reinterpret_cast (&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 (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. // names& ts_ns (vs[0].as ()); // // 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))); d.f (r, vs, *m, *bs, a, t); } return value (move (r)); } // Common thunk for $x.lib_*(, [, ...]) functions. // struct lib_thunk_data { const char* x; void (*f) (void*, strings&, const vector_view&, const module&, const scope&, action, const file&, bool, linfo); }; static value lib_thunk_impl (void* ls, const scope* bs, vector_view vs, const function_overload& f) { const auto& d (*reinterpret_cast (&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 (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. // names& ts_ns (vs[0].as ()); // names& ot_ns (vs[1].as ()); // linfo li; { string t (convert (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 ())) || (la = (f = t.is_a ())) || ( (f = t.is_a ()))) { d.f (ls, r, vs, *m, *bs, a, *f, la, li); } else fail << t << " is not a library target"; } return value (move (r)); } template static value lib_thunk (const scope* bs, vector_view vs, const function_overload& f) { L ls; return lib_thunk_impl (&ls, bs, vs, f); } // @@ Maybe we should provide wrapper functions that return all the // compile options (including from *.?options, mode, etc) and all the // link arguments in the correct order, etc. Can call them: // // compile_options() // link_arguments() // void compile_rule:: functions (function_family& f, const char* x) { // $.lib_poptions(, ) // // 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 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 that this function is not pure. // f.insert (".lib_poptions", false). insert ( &lib_thunk, lib_thunk_data { x, [] (void* ls, strings& r, const vector_view&, const module& m, const scope& bs, action a, const file& l, bool la, linfo li) { m.append_library_options ( *static_cast (ls), r, bs, a, l, la, li); }}); // $.find_system_header() // // Return the header path if the specified header exists in one of the // system header search directories and NULL otherwise. System header // search directories are those that the compiler searches by default // plus directories specified as part of the compiler mode options (but // not *.poptions). // // Note that this function is not pure. // f.insert (".find_system_header", false). insert ( [] (const scope* bs, vector_view vs, const function_overload& f) -> value { const char* x (*reinterpret_cast (&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"; const module* m (rs->find_module (x)); if (m == nullptr) fail << f.name << " called without " << x << " module loaded"; // We can assume the argument is present due to function's types // signature. // auto r (m->find_system_header (convert (move (vs[0])))); return r ? value (move (*r)) : value (nullptr); }, x); } void link_rule:: functions (function_family& f, const char* x) { // $.lib_libs(, [, [, ]]) // // 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 // // absolute - return absolute paths to the libraries // // If the last argument is false, then do not return the specified // libraries themselves. // // 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 that this function is not pure. // f.insert (".lib_libs", false). insert, optional> ( &lib_thunk, lib_thunk_data { x, [] (void* ls, strings& r, const vector_view& vs, const module& m, const scope& bs, action a, const file& l, bool la, linfo li) { lflags lf (0); bool rel (true); if (vs.size () > 2) { for (const name& f: vs[2].as ()) { string s (convert (name (f))); if (s == "whole") lf |= lflag_whole; else if (s == "absolute") rel = false; else fail << "invalid flag '" << s << "'"; } } bool self (vs.size () > 3 ? convert (vs[3]) : true); m.append_libraries ( *static_cast (ls), r, nullptr /* sha256 */, nullptr /* update */, timestamp_unknown, bs, a, l, la, lf, li, nullopt /* for_install */, self, rel); }}); // $.lib_rpaths(, [, [, ]]) // // 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 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 that this function is not pure. // f.insert (".lib_rpaths", false). insert, optional> ( &lib_thunk, lib_thunk_data { x, [] (void* ls, strings& r, const vector_view& vs, const module& m, const scope& bs, action a, const file& l, bool la, linfo li) { bool link (vs.size () > 2 ? convert (vs[2]) : false); bool self (vs.size () > 3 ? convert (vs[3]) : true); m.rpath_libraries (*static_cast (ls), r, bs, a, l, la, li, link, self); }}); // $cxx.obj_modules() // // Return object files corresponding to module interfaces that are used // by the specified object files and that belong to binless libraries. // // 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 object file targets have been matched. // Normally it is used in ad hoc recipes to implement custom linking. // // Note that this function is not pure. // f.insert (".obj_modules", false). insert ( &thunk, thunk_data { x, [] (strings& r, const vector_view&, const module& m, const scope& bs, action a, const target& t) { if (const file* f = t.is_a ()) { if (m.modules) m.append_binless_modules (r, nullptr /* sha256 */, bs, a, *f); } else fail << t << " is not an object file target"; }}); // $.deduplicate_export_libs() // // Deduplicate interface library dependencies by removing libraries that // are also interface dependencies of the specified libraries. This can // result in significantly better build performance for heavily // interface-interdependent library families (for example, like Boost). // Typical usage: // // import intf_libs = ... // import intf_libs += ... // ... // import intf_libs += ... // intf_libs = $cxx.deduplicate_export_libs($intf_libs) // // Notes: // // 1. We only consider unqualified absolute/normalized target names (the // idea is that the installed case will already be deduplicated). // // 2. We assume all the libraries listed are of the module type and only // look for cc.export.libs and .export.libs. // // 3. No member/group selection/linkup: we resolve *.export.libs on // whatever is listed. // // Note that this function is not pure. // f.insert (".deduplicate_export_libs", false). insert ( [] (const scope* bs, vector_view vs, const function_overload& f) -> value { const char* x (*reinterpret_cast (&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"; const module* m (rs->find_module (x)); if (m == nullptr) fail << f.name << " called without " << x << " module loaded"; // We can assume the argument is present due to function's types // signature. // names& r (vs[0].as ()); m->deduplicate_export_libs (*bs, vector (r.begin (), r.end ()), r); return value (move (r)); }, x); // $.find_system_library() // // Return the library path if the specified library exists in one of the // system library search directories. System library search directories // are those that the compiler searches by default plus directories // specified as part of the compiler mode options (but not *.loptions). // // The library can be specified in the same form as expected by the // linker (-lfoo for POSIX, foo.lib for MSVC) or as a complete name. // // Note that this function is not pure. // f.insert (".find_system_library", false). insert ( [] (const scope* bs, vector_view vs, const function_overload& f) -> value { const char* x (*reinterpret_cast (&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"; const module* m (rs->find_module (x)); if (m == nullptr) fail << f.name << " called without " << x << " module loaded"; // We can assume the argument is present due to function's types // signature. // auto r (m->find_system_library (convert (move (vs[0])))); return r ? value (move (*r)) : value (nullptr); }, x); } } }