aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc/link-rule.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/cc/link-rule.hxx')
-rw-r--r--libbuild2/cc/link-rule.hxx278
1 files changed, 229 insertions, 49 deletions
diff --git a/libbuild2/cc/link-rule.hxx b/libbuild2/cc/link-rule.hxx
index db48160..9b491c2 100644
--- a/libbuild2/cc/link-rule.hxx
+++ b/libbuild2/cc/link-rule.hxx
@@ -4,8 +4,6 @@
#ifndef LIBBUILD2_CC_LINK_RULE_HXX
#define LIBBUILD2_CC_LINK_RULE_HXX
-#include <set>
-
#include <libbuild2/types.hxx>
#include <libbuild2/utility.hxx>
@@ -20,11 +18,13 @@ namespace build2
{
namespace cc
{
- class LIBBUILD2_CC_SYMEXPORT link_rule: public simple_rule, virtual common
+ class LIBBUILD2_CC_SYMEXPORT link_rule: public rule, virtual common
{
public:
link_rule (data&&);
+ struct match_data;
+
struct match_result
{
bool seen_x = false;
@@ -32,28 +32,210 @@ namespace build2
bool seen_cc = false;
bool seen_obj = false;
bool seen_lib = false;
+
+ match_result& operator|= (match_result y)
+ {
+ seen_x = seen_x || y.seen_x;
+ seen_c = seen_c || y.seen_c;
+ seen_cc = seen_cc || y.seen_cc;
+ seen_obj = seen_obj || y.seen_obj;
+ seen_lib = seen_lib || y.seen_lib;
+ return *this;
+ }
};
match_result
match (action, const target&, const target*, otype, bool) const;
virtual bool
- match (action, target&, const string&) const override;
+ match (action, target&, const string&, match_extra&) const override;
virtual recipe
- apply (action, target&) const override;
+ apply (action, target&, match_extra&) const override;
target_state
- perform_update (action, const target&) const;
+ perform_update (action, const target&, match_data&) const;
target_state
- perform_clean (action, const target&) const;
+ perform_clean (action, const target&, match_data&) const;
- using simple_rule::match; // To make Clang happy.
+ virtual const target*
+ import (const prerequisite_key&,
+ const optional<string>&,
+ const location&) const override;
- private:
- friend class install_rule;
- friend class libux_install_rule;
+ public:
+ // Library handling.
+ //
+ struct appended_library
+ {
+ static const size_t npos = size_t (~0);
+
+ // Each appended_library represents either a library target or a
+ // library name fragment up to 2 elements long:
+ //
+ // target | name
+ // --------------------------------------------------
+ const void* l1; // library target | library name[1] or NULL
+ const void* l2; // NULL | library name[0]
+
+ size_t begin; // First arg belonging to this library.
+ size_t end; // Past last arg belonging to this library.
+ };
+
+ class appended_libraries: public small_vector<appended_library, 128>
+ {
+ public:
+ // Find existing entry, if any.
+ //
+ appended_library*
+ find (const file& l)
+ {
+ auto i (find_if (begin (), end (),
+ [&l] (const appended_library& al)
+ {
+ return al.l2 == nullptr && al.l1 == &l;
+ }));
+
+ return i != end () ? &*i : nullptr;
+ }
+
+ appended_library*
+ find (const small_vector<reference_wrapper<const string>, 2>& ns)
+ {
+ size_t n (ns.size ());
+
+ if (n > 2)
+ return nullptr;
+
+ auto i (
+ find_if (
+ begin (), end (),
+ [&ns, n] (const appended_library& al)
+ {
+ return al.l2 != nullptr &&
+ *static_cast<const string*> (al.l2) == ns[0].get () &&
+ (n == 2
+ ? (al.l1 != nullptr &&
+ *static_cast<const string*> (al.l1) == ns[1].get ())
+ : al.l1 == nullptr);
+ }));
+
+ return i != end () ? &*i : nullptr;
+ }
+
+ // Find existing or append new entry. If appending new, use the second
+ // argument as the begin value.
+ //
+ appended_library&
+ append (const file& l, size_t b)
+ {
+ if (appended_library* r = find (l))
+ return *r;
+
+ push_back (appended_library {&l, nullptr, b, appended_library::npos});
+ return back ();
+ }
+
+ // Return NULL if no duplicate tracking can be performed for this
+ // library.
+ //
+ appended_library*
+ append (const small_vector<reference_wrapper<const string>, 2>& ns,
+ size_t b)
+ {
+ size_t n (ns.size ());
+
+ if (n > 2)
+ return nullptr;
+
+ if (appended_library* r = find (ns))
+ return r;
+
+ push_back (appended_library {
+ n == 2 ? &ns[1].get () : nullptr, &ns[0].get (),
+ b, appended_library::npos});
+
+ return &back ();
+ }
+
+ // Hoist the elements corresponding to the specified library to the
+ // end.
+ //
+ void
+ hoist (strings& args, appended_library& al)
+ {
+ if (al.begin != al.end)
+ {
+ // Rotate to the left the subrange starting from the first element
+ // of this library and until the end so that the element after the
+ // last element of this library becomes the first element of this
+ // subrange. We also need to adjust begin/end of libraries
+ // affected by the rotation.
+ //
+ rotate (args.begin () + al.begin,
+ args.begin () + al.end,
+ args.end ());
+
+ size_t n (al.end - al.begin);
+
+ for (appended_library& al1: *this)
+ {
+ if (al1.begin >= al.end)
+ {
+ al1.begin -= n;
+ al1.end -= n;
+ }
+ }
+
+ al.end = args.size ();
+ al.begin = al.end - n;
+ }
+ }
+ };
+
+ void
+ append_libraries (appended_libraries&, strings&,
+ sha256*, bool*, timestamp,
+ const scope&, action,
+ const file&, bool, lflags, linfo,
+ optional<bool>, bool = true, bool = true,
+ library_cache* = nullptr) const;
+
+ using rpathed_libraries = small_vector<const file*, 256>;
+
+ void
+ rpath_libraries (rpathed_libraries&, strings&,
+ const scope&,
+ action, const file&, bool, linfo, bool, bool,
+ library_cache* = nullptr) const;
+
+ void
+ rpath_libraries (strings&,
+ const scope&, action,
+ const target&, linfo, bool) const;
+
+ void
+ append_binless_modules (strings&, sha256*,
+ const scope&, action, const file&) const;
+
+ bool
+ deduplicate_export_libs (
+ const scope&,
+ const vector<name>&,
+ names&,
+ vector<reference_wrapper<const name>>* = nullptr) const;
+
+ optional<path>
+ find_system_library (const strings&) const;
+
+ protected:
+ static void
+ functions (function_family&, const char*); // functions.cxx
+
+ // Implementation details.
+ //
+ public:
// Shared library paths.
//
@@ -96,27 +278,36 @@ namespace build2
struct match_data
{
+ explicit
+ match_data (const link_rule& r): rule (r) {}
+
// The "for install" condition is signalled to us by install_rule when
// it is matched for the update operation. It also verifies that if we
// have already been executed, then it was for install.
//
// This has an interesting implication: it means that this rule cannot
- // be used to update targets during match. Specifically, we cannot be
- // executed for group resolution purposes (not a problem) nor as part
- // of the generated source update. The latter case can be a problem:
- // imagine a code generator that itself may need to be updated before
- // it can be used to re-generate some out-of-date source code. As an
- // aside, note that even if we were somehow able to communicate the
- // "for install" in this case, the result of such an update may not
- // actually be "usable" (e.g., not runnable because of the missing
+ // be used to update targets to be installed during match (since we
+ // would notice that they are for install too late). Specifically, we
+ // cannot be executed for group resolution purposes (should not be a
+ // problem) nor as part of the generated source update. The latter
+ // case can be a problem: imagine a source code generator that itself
+ // may need to be updated before it can be used to re-generate some
+ // out-of-date source code (or, worse, both the generator and the
+ // target to be installed depend on the same library).
+ //
+ // As an aside, note that even if we were somehow able to communicate
+ // the "for install" in this case, the result of such an update may
+ // not actually be "usable" (e.g., not runnable because of the missing
// rpaths). There is another prominent case where the result may not
- // be usable: cross-compilation.
+ // be usable: cross-compilation (in fact, if you think about it, "for
+ // install" is quite similar to cross-compilation: we are building for
+ // a foreign "environment" and thus cannot execute the results of the
+ // build).
//
- // So the current (admittedly fuzzy) thinking is that a project shall
- // not try to use its own build for update since it may not be usable
- // (because of cross-compilations, being "for install", etc). Instead,
- // it should rely on another, "usable" build of itself (this, BTW, is
- // related to bpkg's build-time vs run-time dependencies).
+ // So the current thinking is that a project shall not try to use its
+ // own "for install" (or, naturally, cross-compilation) build for
+ // update since it may not be usable. Instead, it should rely on
+ // another, "usable" build.
//
optional<bool> for_install;
@@ -124,39 +315,28 @@ namespace build2
size_t start; // Parallel prerequisites/prerequisite_targets start.
link_rule::libs_paths libs_paths;
- };
- // Library handling.
- //
- void
- append_libraries (strings&,
- const file&, bool, lflags,
- const scope&, action, linfo) const;
+ const link_rule& rule;
- 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;
+ target_state
+ operator() (action a, const target& t)
+ {
+ return a == perform_update_id
+ ? rule.perform_update (a, t, *this)
+ : rule.perform_clean (a, t, *this);
+ }
+ };
// Windows rpath emulation (windows-rpath.cxx).
//
+ private:
struct windows_dll
{
- const string& dll;
- const string* pdb; // NULL if none.
- string pdb_storage;
-
- bool operator< (const windows_dll& y) const {return dll < y.dll;}
+ reference_wrapper<const string> dll;
+ string pdb; // Empty if none.
};
- using windows_dlls = std::set<windows_dll>;
+ using windows_dlls = vector<windows_dll>;
timestamp
windows_rpath_timestamp (const file&,