aboutsummaryrefslogtreecommitdiff
path: root/build2/cc/link-rule.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-01-20 13:46:11 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-02-03 14:35:45 +0200
commit934f2a9a90c5cad3cdc8a66b50c17827a3ddbcee (patch)
treef35f106e5369e98350327c79080c571195234c0b /build2/cc/link-rule.cxx
parent280f4a5bf787587227ca193cd59c6bd74091db70 (diff)
Get rid of action rule override semantics
Instead we now have two more or less separate match states for outer and inner parts of an action.
Diffstat (limited to 'build2/cc/link-rule.cxx')
-rw-r--r--build2/cc/link-rule.cxx2104
1 files changed, 2104 insertions, 0 deletions
diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx
new file mode 100644
index 0000000..d06a835
--- /dev/null
+++ b/build2/cc/link-rule.cxx
@@ -0,0 +1,2104 @@
+// file : build2/cc/link-rule.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/cc/link-rule.hxx>
+
+#include <map>
+#include <cstdlib> // exit()
+
+#include <libbutl/path-map.mxx>
+#include <libbutl/filesystem.mxx> // file_exists()
+
+#include <build2/depdb.hxx>
+#include <build2/scope.hxx>
+#include <build2/context.hxx>
+#include <build2/variable.hxx>
+#include <build2/algorithm.hxx>
+#include <build2/filesystem.hxx>
+#include <build2/diagnostics.hxx>
+
+#include <build2/bin/target.hxx>
+
+#include <build2/cc/target.hxx> // c, pc*
+#include <build2/cc/utility.hxx>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace cc
+ {
+ using namespace bin;
+
+ link_rule::
+ link_rule (data&& d)
+ : common (move (d)),
+ rule_id (string (x) += ".link 1")
+ {
+ static_assert (sizeof (match_data) <= target::data_size,
+ "insufficient space");
+ }
+
+ bool link_rule::
+ match (action a, target& t, const string& hint) const
+ {
+ tracer trace (x, "link_rule::match");
+
+ // NOTE: may be called multiple times (see install rules).
+
+ ltype lt (link_type (t));
+ otype ot (lt.type);
+
+ // If this is a library, link-up to our group (this is the target group
+ // protocol which means this can be done whether we match or not).
+ //
+ if (lt.library ())
+ {
+ if (t.group == nullptr)
+ t.group = &search (t,
+ lt.utility ? libu::static_type : lib::static_type,
+ t.dir, t.out, t.name);
+ }
+
+ // Scan prerequisites and see if we can work with what we've got. Note
+ // that X could be C. We handle this by always checking for X first.
+ //
+ // Note also that we treat bmi{} as obj{}.
+ //
+ bool seen_x (false), seen_c (false), seen_obj (false), seen_lib (false);
+
+ for (prerequisite_member p: group_prerequisite_members (a, t))
+ {
+ if (p.is_a (x_src) || (x_mod != nullptr && p.is_a (*x_mod)))
+ {
+ seen_x = seen_x || true;
+ }
+ else if (p.is_a<c> ())
+ {
+ seen_c = seen_c || true;
+ }
+ else if (p.is_a<obj> () || p.is_a<bmi> ())
+ {
+ seen_obj = seen_obj || true;
+ }
+ else if (p.is_a<obje> () || p.is_a<bmie> ())
+ {
+ if (ot != otype::e)
+ fail << p.type ().name << "{} as prerequisite of " << t;
+
+ seen_obj = seen_obj || true;
+ }
+ else if (p.is_a<obja> () || p.is_a<bmia> ())
+ {
+ if (ot != otype::a)
+ fail << p.type ().name << "{} as prerequisite of " << t;
+
+ seen_obj = seen_obj || true;
+ }
+ else if (p.is_a<objs> () || p.is_a<bmis> ())
+ {
+ if (ot != otype::s)
+ fail << p.type ().name << "{} as prerequisite of " << t;
+
+ seen_obj = seen_obj || true;
+ }
+ else if (p.is_a<libx> () ||
+ p.is_a<liba> () ||
+ p.is_a<libs> () ||
+ p.is_a<libux> ())
+ {
+ seen_lib = seen_lib || true;
+ }
+ // If this is some other c-common source (say C++ in a C rule), then
+ // it will most definitely need to be compiled but we can't do that.
+ //
+ else if (p.is_a<cc> ())
+ return false;
+ }
+
+ if (!(seen_x || seen_c || seen_obj || seen_lib))
+ return false;
+
+ // We will only chain a C source if there is also an X source or we were
+ // explicitly told to.
+ //
+ if (seen_c && !seen_x && hint < x)
+ {
+ l4 ([&]{trace << "C prerequisite without " << x_lang << " or hint";});
+ return false;
+ }
+
+ return true;
+ }
+
+ auto link_rule::
+ derive_libs_paths (file& ls, const char* pfx, const char* sfx) const
+ -> libs_paths
+ {
+ const char* ext (nullptr);
+
+ bool win (tclass == "windows");
+
+ if (win)
+ {
+ if (tsys == "mingw32")
+ {
+ if (pfx == nullptr)
+ pfx = "lib";
+ }
+
+ ext = "dll";
+ }
+ else
+ {
+ if (pfx == nullptr)
+ pfx = "lib";
+
+ if (tclass == "macos")
+ ext = "dylib";
+ else
+ ext = "so";
+ }
+
+ // First sort out which extension we are using.
+ //
+ const string& e (ls.derive_extension (ext));
+
+ auto append_ext = [&e] (path& p)
+ {
+ if (!e.empty ())
+ {
+ p += '.';
+ p += e;
+ }
+ };
+
+ // Figure out the version.
+ //
+ string v;
+ using verion_map = map<string, string>;
+ if (const verion_map* m = cast_null<verion_map> (ls["bin.lib.version"]))
+ {
+ // First look for the target system.
+ //
+ auto i (m->find (tsys));
+
+ // Then look for the target class.
+ //
+ if (i == m->end ())
+ i = m->find (tclass);
+
+ // Then look for the wildcard. Since it is higly unlikely one can have
+ // a version that will work across platforms, this is only useful to
+ // say "all others -- no version".
+ //
+ if (i == m->end ())
+ i = m->find ("*");
+
+ // At this stage the only platform-specific version we support is the
+ // "no version" override.
+ //
+ if (i != m->end () && !i->second.empty ())
+ fail << i->first << "-specific bin.lib.version not yet supported";
+
+ // Finally look for the platform-independent version.
+ //
+ if (i == m->end ())
+ i = m->find ("");
+
+ // If we didn't find anything, fail. If the bin.lib.version was
+ // specified, then it should explicitly handle all the targets.
+ //
+ if (i == m->end ())
+ fail << "no version for " << ctgt << " in bin.lib.version" <<
+ info << "considere adding " << tsys << "@<ver> or " << tclass
+ << "@<ver>";
+
+ v = i->second;
+ }
+
+ // Now determine the paths.
+ //
+ path lk, so, in;
+
+ // We start with the basic path.
+ //
+ path b (ls.dir);
+ path cp; // Clean pattern.
+ {
+ if (pfx == nullptr || pfx[0] == '\0')
+ {
+ b /= ls.name;
+ }
+ else
+ {
+ b /= pfx;
+ b += ls.name;
+ }
+
+ cp = b;
+ cp += "?*"; // Don't match empty (like the libfoo.so symlink).
+
+ if (sfx != nullptr)
+ {
+ b += sfx;
+ cp += sfx;
+ }
+ }
+
+ append_ext (cp);
+
+ // On Windows the real path is to libs{} and the link path is to the
+ // import library.
+ //
+ if (win)
+ {
+ // Usually on Windows the import library is called the same as the DLL
+ // but with the .lib extension. Which means it clashes with the static
+ // library. Instead of decorating the static library name with ugly
+ // suffixes (as is customary), let's use the MinGW approach (one must
+ // admit it's quite elegant) and call it .dll.lib.
+ //
+ lk = b;
+ append_ext (lk);
+
+ libi& li (ls.member->as<libi> ()); // Note: libi is locked.
+ lk = li.derive_path (move (lk), tsys == "mingw32" ? "a" : "lib");
+ }
+ else if (!v.empty ())
+ {
+ lk = b;
+ append_ext (lk);
+ }
+
+ if (!v.empty ())
+ b += v;
+
+ const path& re (ls.derive_path (move (b)));
+
+ return libs_paths {move (lk), move (so), move (in), &re, move (cp)};
+ }
+
+ recipe link_rule::
+ apply (action a, target& xt) const
+ {
+ tracer trace (x, "link_rule::apply");
+
+ file& t (xt.as<file> ());
+
+ // Note that for_install is signalled by install_rule and therefore
+ // can only be relied upon during execute.
+ //
+ match_data& md (t.data (match_data ()));
+
+ const scope& bs (t.base_scope ());
+ const scope& rs (*bs.root_scope ());
+
+ ltype lt (link_type (t));
+ otype ot (lt.type);
+ linfo li (link_info (bs, ot));
+
+ // Set the library type (C, C++, etc).
+ //
+ if (lt.library ())
+ t.vars.assign (c_type) = string (x);
+
+ // Derive file name(s) and add ad hoc group members.
+ //
+ {
+ target_lock libi; // Have to hold until after PDB member addition.
+
+ const char* e (nullptr); // Extension.
+ const char* p (nullptr); // Prefix.
+ const char* s (nullptr); // Suffix.
+
+ if (lt.utility)
+ {
+ // These are all static libraries with names indicating the kind of
+ // object files they contain (similar to how we name object files
+ // themselves). We add the 'u' extension to avoid clashes with
+ // real libraries/import stubs.
+ //
+ // libue libhello.u.a hello.exe.u.lib
+ // libua libhello.a.u.a hello.lib.u.lib
+ // libus libhello.so.u.a hello.dll.u.lib hello.dylib.u.lib
+ //
+ // Note that we currently don't add bin.lib.{prefix,suffix} since
+ // these are not installed.
+ //
+ if (tsys == "win32-msvc")
+ {
+ switch (ot)
+ {
+ case otype::e: e = "exe.u.lib"; break;
+ case otype::a: e = "lib.u.lib"; break;
+ case otype::s: e = "dll.u.lib"; break;
+ }
+ }
+ else
+ {
+ p = "lib";
+
+ if (tsys == "mingw32")
+ {
+ switch (ot)
+ {
+ case otype::e: e = "exe.u.a"; break;
+ case otype::a: e = "a.u.a"; break;
+ case otype::s: e = "dll.u.a"; break;
+ }
+
+ }
+ else if (tsys == "darwin")
+ {
+ switch (ot)
+ {
+ case otype::e: e = "u.a"; break;
+ case otype::a: e = "a.u.a"; break;
+ case otype::s: e = "dylib.u.a"; break;
+ }
+ }
+ else
+ {
+ switch (ot)
+ {
+ case otype::e: e = "u.a"; break;
+ case otype::a: e = "a.u.a"; break;
+ case otype::s: e = "so.u.a"; break;
+ }
+ }
+ }
+
+ t.derive_path (e, p, s);
+ }
+ else
+ {
+ if (auto l = t[ot == otype::e ? "bin.exe.prefix" : "bin.lib.prefix"])
+ p = cast<string> (l).c_str ();
+ if (auto l = t[ot == otype::e ? "bin.exe.suffix" : "bin.lib.suffix"])
+ s = cast<string> (l).c_str ();
+
+ switch (ot)
+ {
+ case otype::e:
+ {
+ if (tclass == "windows")
+ e = "exe";
+ else
+ e = "";
+
+ t.derive_path (e, p, s);
+ break;
+ }
+ case otype::a:
+ {
+ if (tsys == "win32-msvc")
+ e = "lib";
+ else
+ {
+ if (p == nullptr) p = "lib";
+ e = "a";
+ }
+
+ t.derive_path (e, p, s);
+ break;
+ }
+ case otype::s:
+ {
+ // On Windows libs{} is an ad hoc group. The libs{} itself is
+ // the DLL and we add libi{} import library as its member.
+ //
+ if (tclass == "windows")
+ libi = add_adhoc_member<bin::libi> (a, t);
+
+ md.libs_data = derive_libs_paths (t, p, s);
+
+ if (libi)
+ match_recipe (libi, group_recipe); // Set recipe and unlock.
+
+ break;
+ }
+ }
+
+ // Add VC's .pdb. Note that we are looking for the link.exe /DEBUG
+ // option.
+ //
+ if (ot != otype::a &&
+ tsys == "win32-msvc" &&
+ (find_option ("/DEBUG", t, c_loptions, true) ||
+ find_option ("/DEBUG", t, x_loptions, true)))
+ {
+ // Note: add after the import library if any.
+ //
+ target_lock pdb (
+ add_adhoc_member (a, t, *bs.find_target_type ("pdb")));
+
+ // We call it foo.{exe,dll}.pdb rather than just foo.pdb because
+ // we can have both foo.exe and foo.dll in the same directory.
+ //
+ pdb.target->as<file> ().derive_path (t.path (), "pdb");
+
+ match_recipe (pdb, group_recipe); // Set recipe and unlock.
+ }
+
+ // Add pkg-config's .pc file.
+ //
+ // Note that we do it here regardless of whether we are installing
+ // or not for two reasons. Firstly, it is not easy to detect this
+ // situation in apply() since the for_install hasn't yet been
+ // communicated by install_rule. Secondly, always having the member
+ // takes care of cleanup automagically. The actual generation
+ // happens in perform_update() below.
+ //
+ if (ot != otype::e)
+ {
+ target_lock pc (
+ add_adhoc_member (
+ a, t,
+ ot == otype::a ? pca::static_type : pcs::static_type));
+
+ // Note that here we always use the lib name prefix, even on
+ // Windows with VC. The reason is the user needs a consistent name
+ // across platforms by which they can refere to the library. This
+ // is also the reason why we use the static/shared suffixes rather
+ // that a./.lib/.so/.dylib/.dll.
+ //
+ pc.target->as<file> ().derive_path (nullptr,
+ (p == nullptr ? "lib" : p),
+ s);
+
+ match_recipe (pc, group_recipe); // Set recipe and unlock.
+ }
+ }
+ }
+
+ // Inject dependency on the output directory.
+ //
+ inject_fsdir (a, t);
+
+ // Process prerequisites, pass 1: search and match prerequisite
+ // libraries, search obj/bmi{} targets, and search targets we do rule
+ // chaining for.
+ //
+ // We do libraries first in order to indicate that we will execute these
+ // targets before matching any of the obj/bmi{}. This makes it safe for
+ // compile::apply() to unmatch them and therefore not to hinder
+ // parallelism.
+ //
+ // We also create obj/bmi{} chain targets because we need to add
+ // (similar to lib{}) all the bmi{} as prerequisites to all the other
+ // obj/bmi{} that we are creating. Note that this doesn't mean that the
+ // compile rule will actually treat them all as prerequisite targets.
+ // Rather, they are used to resolve actual module imports. We don't
+ // really have to search obj{} targets here but it's the same code so we
+ // do it here to avoid duplication.
+ //
+ // Also, when cleaning, we ignore prerequisites that are not in the same
+ // or a subdirectory of our project root.
+ //
+ optional<dir_paths> usr_lib_dirs; // Extract lazily.
+ compile_target_types tt (compile_types (ot));
+
+ auto skip = [&a, &rs] (const target*& pt)
+ {
+ if (a.operation () == clean_id && !pt->dir.sub (rs.out_path ()))
+ pt = nullptr;
+
+ return pt == nullptr;
+ };
+
+ auto& pts (t.prerequisite_targets[a]);
+ size_t start (pts.size ());
+
+ for (prerequisite_member p: group_prerequisite_members (a, t))
+ {
+ // We pre-allocate a NULL slot for each (potential; see clean)
+ // prerequisite target.
+ //
+ pts.push_back (nullptr);
+ const target*& pt (pts.back ());
+
+ uint8_t m (0); // Mark: lib (0), src (1), mod (2), obj/bmi (3).
+
+ bool mod (x_mod != nullptr && p.is_a (*x_mod));
+
+ if (mod || p.is_a (x_src) || p.is_a<c> ())
+ {
+ // Rule chaining, part 1.
+ //
+
+ // Which scope shall we use to resolve the root? Unlikely, but
+ // possible, the prerequisite is from a different project
+ // altogether. So we are going to use the target's project.
+ //
+
+ // If the source came from the lib{} group, then create the obj{}
+ // group and add the source as a prerequisite of the obj{} group,
+ // not the obj*{} member. This way we only need one prerequisite
+ // for, say, both liba{} and libs{}. The same goes for bmi{}.
+ //
+ bool group (!p.prerequisite.belongs (t)); // Group's prerequisite.
+
+ const target_type& rtt (mod
+ ? (group ? bmi::static_type : tt.bmi)
+ : (group ? obj::static_type : tt.obj));
+
+ const prerequisite_key& cp (p.key ()); // Source key.
+
+ // Come up with the obj*/bmi*{} target. The source prerequisite
+ // directory can be relative (to the scope) or absolute. If it is
+ // relative, then use it as is. If absolute, then translate it to
+ // the corresponding directory under out_root. While the source
+ // directory is most likely under src_root, it is also possible it
+ // is under out_root (e.g., generated source).
+ //
+ dir_path d;
+ {
+ const dir_path& cpd (*cp.tk.dir);
+
+ if (cpd.relative () || cpd.sub (rs.out_path ()))
+ d = cpd;
+ else
+ {
+ if (!cpd.sub (rs.src_path ()))
+ fail << "out of project prerequisite " << cp <<
+ info << "specify corresponding " << rtt.name << "{} "
+ << "target explicitly";
+
+ d = rs.out_path () / cpd.leaf (rs.src_path ());
+ }
+ }
+
+ // obj/bmi{} is always in the out tree. Note that currently it could
+ // be the group -- we will pick a member in part 2 below.
+ //
+ pt = &search (t, rtt, d, dir_path (), *cp.tk.name, nullptr, cp.scope);
+
+ // If we shouldn't clean obj{}, then it is fair to assume we
+ // shouldn't clean the source either (generated source will be in
+ // the same directory as obj{} and if not, well, go find yourself
+ // another build system ;-)).
+ //
+ if (skip (pt))
+ continue;
+
+ m = mod ? 2 : 1;
+ }
+ else if (p.is_a<libx> () ||
+ p.is_a<liba> () ||
+ p.is_a<libs> () ||
+ p.is_a<libux> ())
+ {
+ // Handle imported libraries.
+ //
+ // Note that since the search is rule-specific, we don't cache the
+ // target in the prerequisite.
+ //
+ if (p.proj ())
+ pt = search_library (
+ a, sys_lib_dirs, usr_lib_dirs, p.prerequisite);
+
+ // The rest is the same basic logic as in search_and_match().
+ //
+ if (pt == nullptr)
+ pt = &p.search (t);
+
+ if (skip (pt))
+ continue;
+
+ // If this is the lib{}/libu{} group, then pick the appropriate
+ // member.
+ //
+ if (const libx* l = pt->is_a<libx> ())
+ pt = &link_member (*l, a, li);
+ }
+ else
+ {
+ // If this is the obj{} or bmi{} target group, then pick the
+ // appropriate member.
+ //
+ if (p.is_a<obj> ()) pt = &search (t, tt.obj, p.key ());
+ else if (p.is_a<bmi> ()) pt = &search (t, tt.bmi, p.key ());
+ else pt = &p.search (t);
+
+ if (skip (pt))
+ continue;
+
+ m = 3;
+ }
+
+ mark (pt, m);
+ }
+
+ // Match lib{} (the only unmarked) in parallel and wait for completion.
+ //
+ match_members (a, t, pts, start);
+
+ // Process prerequisites, pass 2: finish rule chaining but don't start
+ // matching anything yet since that may trigger recursive matching of
+ // bmi{} targets we haven't completed yet. Hairy, I know.
+ //
+
+ // Parallel prerequisite_targets loop.
+ //
+ size_t i (start), n (pts.size ());
+ for (prerequisite_member p: group_prerequisite_members (a, t))
+ {
+ const target*& pt (pts[i].target);
+ uintptr_t& pd (pts[i++].data);
+
+ if (pt == nullptr)
+ continue;
+
+ uint8_t m (unmark (pt)); // New mark: completion (1), verfication (2).
+
+ if (m == 3) // obj/bmi{}
+ m = 1; // Just completion.
+ else if (m == 1 || m == 2) // Source/module chain.
+ {
+ bool mod (m == 2);
+
+ m = 1;
+
+ const target& rt (*pt);
+ bool group (!p.prerequisite.belongs (t)); // Group's prerequisite.
+
+ // If we have created a obj/bmi{} target group, pick one of its
+ // members; the rest would be primarily concerned with it.
+ //
+ pt =
+ group
+ ? &search (t, (mod ? tt.bmi : tt.obj), rt.dir, rt.out, rt.name)
+ : &rt;
+
+ // If this obj*{} already has prerequisites, then verify they are
+ // "compatible" with what we are doing here. Otherwise, synthesize
+ // the dependency. Note that we may also end up synthesizing with
+ // someone beating up to it. In this case also verify.
+ //
+ bool verify (true);
+
+ if (!pt->has_prerequisites ())
+ {
+ prerequisites ps;
+ ps.push_back (p.as_prerequisite ()); // Source.
+
+ // Add our lib*{} (see the export.* machinery for details) and
+ // bmi*{} (both original and chained; see module search logic)
+ // prerequisites.
+ //
+ // Note that we don't resolve lib{} to liba{}/libs{} here
+ // instead leaving it to whomever (e.g., the compile rule) will
+ // be needing *.export.*. One reason for doing it there is that
+ // the object target might be specified explicitly by the user
+ // in which case they will have to specify the set of lib{}
+ // prerequisites and it's much cleaner to do as lib{} rather
+ // than liba{}/libs{}.
+ //
+ // Initially, we were only adding imported libraries, but there
+ // is a problem with this approach: the non-imported library
+ // might depend on the imported one(s) which we will never "see"
+ // unless we start with this library.
+ //
+ // Note: have similar logic in make_module_sidebuild().
+ //
+ size_t j (start);
+ for (prerequisite_member p: group_prerequisite_members (a, t))
+ {
+ const target* pt (pts[j++]);
+
+ if (p.is_a<libx> () ||
+ p.is_a<liba> () || p.is_a<libs> () || p.is_a<libux> () ||
+ p.is_a<bmi> () || p.is_a (tt.bmi))
+ {
+ ps.push_back (p.as_prerequisite ());
+ }
+ else if (x_mod != nullptr && p.is_a (*x_mod)) // Chained module.
+ {
+ // Searched during pass 1 but can be NULL or marked.
+ //
+ if (pt != nullptr && i != j) // Don't add self (note: both +1).
+ {
+ // This is sticky: pt might have come before us and if it
+ // was a group, then we would have picked up a member. So
+ // here we may have to "unpick" it.
+ //
+ bool group (j < i && !p.prerequisite.belongs (t));
+
+ unmark (pt);
+ ps.push_back (prerequisite (group ? *pt->group : *pt));
+ }
+ }
+ }
+
+ // Note: add to the group, not the member.
+ //
+ verify = !rt.prerequisites (move (ps));
+ }
+
+ if (verify)
+ {
+ // This gets a bit tricky. We need to make sure the source files
+ // are the same which we can only do by comparing the targets to
+ // which they resolve. But we cannot search ot's prerequisites --
+ // only the rule that matches can. Note, however, that if all this
+ // works out, then our next step is to match the obj*{} target. If
+ // things don't work out, then we fail, in which case searching
+ // and matching speculatively doesn't really hurt. So we start the
+ // async match here and finish this verification in the "harvest"
+ // loop below.
+ //
+ const target_type& rtt (mod
+ ? (group ? bmi::static_type : tt.bmi)
+ : (group ? obj::static_type : tt.obj));
+
+ bool src (false);
+ for (prerequisite_member p1: group_prerequisite_members (a, *pt))
+ {
+ // Most of the time we will have just a single source so fast-
+ // path that case.
+ //
+ if (p1.is_a (mod ? *x_mod : x_src) || p1.is_a<c> ())
+ {
+ src = true;
+ continue; // Check the rest of the prerequisites.
+ }
+
+ // Ignore some known target types (fsdir, headers, libraries,
+ // modules).
+ //
+ if (p1.is_a<fsdir> () ||
+ p1.is_a<libx> () ||
+ p1.is_a<liba> () || p1.is_a<libs> () || p1.is_a<libux> () ||
+ p1.is_a<bmi> () ||
+ p1.is_a<bmie> () || p1.is_a<bmia> () || p1.is_a<bmis> () ||
+ (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) ||
+ (p.is_a<c> () && p1.is_a<h> ()))
+ continue;
+
+ fail << "synthesized dependency for prerequisite " << p
+ << " would be incompatible with existing target " << *pt <<
+ info << "unexpected existing prerequisite type " << p1 <<
+ info << "specify corresponding " << rtt.name << "{} "
+ << "dependency explicitly";
+ }
+
+ if (!src)
+ fail << "synthesized dependency for prerequisite " << p
+ << " would be incompatible with existing target " << *pt <<
+ info << "no existing c/" << x_name << " source prerequisite" <<
+ info << "specify corresponding " << rtt.name << "{} "
+ << "dependency explicitly";
+
+ m = 2; // Needs verification.
+ }
+ }
+ else // lib*{}
+ {
+ // If this is a static library, see if we need to link it whole.
+ // Note that we have to do it after match since we rely on the
+ // group link-up.
+ //
+ bool u;
+ if ((u = pt->is_a<libux> ()) || pt->is_a<liba> ())
+ {
+ const variable& var (var_pool["bin.whole"]); // @@ Cache.
+
+ // See the bin module for the lookup semantics discussion. Note
+ // that the variable is not overridable so we omit find_override()
+ // calls.
+ //
+ lookup l (p.prerequisite.vars[var]);
+
+ if (!l.defined ())
+ l = pt->find_original (var, true).first;
+
+ if (!l.defined ())
+ {
+ bool g (pt->group != nullptr);
+ l = bs.find_original (var,
+ &pt->type (),
+ &pt->name,
+ (g ? &pt->group->type () : nullptr),
+ (g ? &pt->group->name : nullptr)).first;
+ }
+
+ if (l ? cast<bool> (*l) : u)
+ pd |= lflag_whole;
+ }
+ }
+
+ mark (pt, m);
+ }
+
+ // Process prerequisites, pass 3: match everything and verify chains.
+ //
+
+ // Wait with unlocked phase to allow phase switching.
+ //
+ wait_guard wg (target::count_busy (), t[a].task_count, true);
+
+ for (i = start; i != n; ++i)
+ {
+ const target*& pt (pts[i]);
+
+ if (pt == nullptr)
+ continue;
+
+ if (uint8_t m = unmark (pt))
+ {
+ match_async (a, *pt, target::count_busy (), t[a].task_count);
+ mark (pt, m);
+ }
+ }
+
+ wg.wait ();
+
+ // The "harvest" loop: finish matching the targets we have started. Note
+ // that we may have bailed out early (thus the parallel i/n for-loop).
+ //
+ i = start;
+ for (prerequisite_member p: group_prerequisite_members (a, t))
+ {
+ const target*& pt (pts[i++]);
+
+ // Skipped or not marked for completion.
+ //
+ uint8_t m;
+ if (pt == nullptr || (m = unmark (pt)) == 0)
+ continue;
+
+ build2::match (a, *pt);
+
+ // Nothing else to do if not marked for verification.
+ //
+ if (m == 1)
+ continue;
+
+ // Finish verifying the existing dependency (which is now matched)
+ // compared to what we would have synthesized.
+ //
+ bool mod (x_mod != nullptr && p.is_a (*x_mod));
+
+ for (prerequisite_member p1: group_prerequisite_members (a, *pt))
+ {
+ if (p1.is_a (mod ? *x_mod : x_src) || p1.is_a<c> ())
+ {
+ // Searching our own prerequisite is ok, p1 must already be
+ // resolved.
+ //
+ if (&p.search (t) != &p1.search (*pt))
+ {
+ bool group (!p.prerequisite.belongs (t));
+
+ const target_type& rtt (mod
+ ? (group ? bmi::static_type : tt.bmi)
+ : (group ? obj::static_type : tt.obj));
+
+ fail << "synthesized dependency for prerequisite " << p << " "
+ << "would be incompatible with existing target " << *pt <<
+ info << "existing prerequisite " << p1 << " does not match "
+ << p <<
+ info << "specify corresponding " << rtt.name << "{} "
+ << "dependency explicitly";
+ }
+
+ break;
+ }
+ }
+ }
+
+ switch (a)
+ {
+ case perform_update_id: return [this] (action a, const target& t)
+ {
+ return perform_update (a, t);
+ };
+ case perform_clean_id: return [this] (action a, const target& t)
+ {
+ return perform_clean (a, t);
+ };
+ default: return noop_recipe; // Configure update.
+ }
+ }
+
+ void link_rule::
+ append_libraries (strings& args,
+ const file& l, bool la, lflags lf,
+ const scope& bs, action a, linfo li) const
+ {
+ // Note: lack of the "small function object" optimization will really
+ // kill us here since we are called in a loop.
+ //
+ auto imp = [] (const file&, bool la) {return la;};
+
+ auto lib = [&args, this] (const file* l, const string& p, lflags f, bool)
+ {
+ if (l != nullptr)
+ {
+ // On Windows a shared library is a DLL with the import library as a
+ // first ad hoc group member. MinGW though can link directly to DLLs
+ // (see search_library() for details).
+ //
+ if (l->member != nullptr && l->is_a<libs> () && tclass == "windows")
+ l = &l->member->as<file> ();
+
+ string p (relative (l->path ()).string ());
+
+ if (f & lflag_whole)
+ {
+ if (tsys == "win32-msvc")
+ {
+ p.insert (0, "/WHOLEARCHIVE:"); // Only available from VC14U2.
+ }
+ else if (tsys == "darwin")
+ {
+ p.insert (0, "-Wl,-force_load,");
+ }
+ else
+ {
+ args.push_back ("-Wl,--whole-archive");
+ args.push_back (move (p));
+ args.push_back ("-Wl,--no-whole-archive");
+ return;
+ }
+ }
+
+ args.push_back (move (p));
+ }
+ else
+ args.push_back (p);
+ };
+
+ auto opt = [&args, this] (
+ const file& l, const string& t, bool com, bool exp)
+ {
+ // If we need an interface value, then use the group (lib{}).
+ //
+ if (const target* g = exp && l.is_a<libs> () ? l.group : &l)
+ {
+ const variable& var (
+ com
+ ? (exp ? c_export_loptions : c_loptions)
+ : (t == x
+ ? (exp ? x_export_loptions : x_loptions)
+ : var_pool[t + (exp ? ".export.loptions" : ".loptions")]));
+
+ append_options (args, *g, var);
+ }
+ };
+
+ process_libraries (
+ a, bs, li, sys_lib_dirs, l, la, lf, imp, lib, opt, true);
+ }
+
+ void link_rule::
+ hash_libraries (sha256& cs,
+ bool& update, timestamp mt,
+ const file& l, bool la, lflags lf,
+ const scope& bs, action a, linfo li) const
+ {
+ auto imp = [] (const file&, bool la) {return la;};
+
+ struct data
+ {
+ sha256& cs;
+ bool& update;
+ timestamp mt;
+ } d {cs, update, mt};
+
+ auto lib = [&d, this] (const file* l, const string& p, lflags f, bool)
+ {
+ if (l != nullptr)
+ {
+ // Check if this library renders us out of date.
+ //
+ d.update = d.update || l->newer (d.mt);
+
+ // On Windows a shared library is a DLL with the import library as a
+ // first ad hoc group member. MinGW though can link directly to DLLs
+ // (see search_library() for details).
+ //
+ if (l->member != nullptr && l->is_a<libs> () && tclass == "windows")
+ l = &l->member->as<file> ();
+
+ d.cs.append (f);
+ d.cs.append (l->path ().string ());
+ }
+ else
+ d.cs.append (p);
+ };
+
+ auto opt = [&cs, this] (
+ const file& l, const string& t, bool com, bool exp)
+ {
+ if (const target* g = exp && l.is_a<libs> () ? l.group : &l)
+ {
+ const variable& var (
+ com
+ ? (exp ? c_export_loptions : c_loptions)
+ : (t == x
+ ? (exp ? x_export_loptions : x_loptions)
+ : var_pool[t + (exp ? ".export.loptions" : ".loptions")]));
+
+ hash_options (cs, *g, var);
+ }
+ };
+
+ process_libraries (
+ a, bs, li, sys_lib_dirs, l, la, lf, imp, lib, opt, true);
+ }
+
+ void link_rule::
+ rpath_libraries (strings& args,
+ const target& t,
+ const scope& bs,
+ action a,
+ linfo li,
+ bool for_install) const
+ {
+ // Use -rpath-link on targets that support it (Linux, *BSD). Note
+ // that we don't really need it for top-level libraries.
+ //
+ if (for_install)
+ {
+ if (tclass != "linux" && tclass != "bsd")
+ return;
+ }
+
+ auto imp = [for_install] (const file& l, bool la)
+ {
+ // If we are not installing, then we only need to rpath interface
+ // libraries (they will include rpath's for their implementations)
+ // Otherwise, we have to do this recursively. In both cases we also
+ // want to see through utility libraries.
+ //
+ // The rpath-link part is tricky: ideally we would like to get only
+ // implementations and only of shared libraries. We are not interested
+ // in interfaces because we are linking their libraries explicitly.
+ // However, in our model there is no such thing as "implementation
+ // only"; it is either interface or interface and implementation. So
+ // we are going to rpath-link all of them which should be harmless
+ // except for some noise on the command line.
+ //
+ //
+ return (for_install ? !la : false) || l.is_a<libux> ();
+ };
+
+ // Package the data to keep within the 2-pointer small std::function
+ // optimization limit.
+ //
+ struct
+ {
+ strings& args;
+ bool for_install;
+ } d {args, for_install};
+
+ auto lib = [&d, this] (const file* l, const string& f, lflags, bool sys)
+ {
+ // We don't rpath system libraries. Why, you may ask? There are many
+ // good reasons and I have them written on a napkin somewhere...
+ //
+ if (sys)
+ return;
+
+ if (l != nullptr)
+ {
+ if (!l->is_a<libs> ())
+ return;
+ }
+ else
+ {
+ // This is an absolute path and we need to decide whether it is
+ // a shared or static library. Doesn't seem there is anything
+ // better than checking for a platform-specific extension (maybe
+ // we should cache it somewhere).
+ //
+ size_t p (path::traits::find_extension (f));
+
+ if (p == string::npos)
+ return;
+
+ ++p; // Skip dot.
+
+ bool c (true);
+ const char* e;
+
+ if (tclass == "windows") {e = "dll"; c = false;}
+ else if (tsys == "darwin") e = "dylib";
+ else e = "so";
+
+ if ((c
+ ? f.compare (p, string::npos, e)
+ : casecmp (f.c_str () + p, e)) != 0)
+ return;
+ }
+
+ // Ok, if we are here then it means we have a non-system, shared
+ // library and its absolute path is in f.
+ //
+ string o (d.for_install ? "-Wl,-rpath-link," : "-Wl,-rpath,");
+
+ size_t p (path::traits::rfind_separator (f));
+ assert (p != string::npos);
+
+ o.append (f, 0, (p != 0 ? p : 1)); // Don't include trailing slash.
+ 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 string&, lflags, bool)> libf (lib);
+
+ for (const prerequisite_target& pt: t.prerequisite_targets[a])
+ {
+ bool la;
+ const file* f;
+
+ if ((la = (f = pt->is_a<liba> ())) ||
+ (la = (f = pt->is_a<libux> ())) ||
+ ( f = pt->is_a<libs> ()))
+ {
+ if (!for_install && !la)
+ {
+ // Top-level sharen library dependency. 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);
+ }
+ }
+ }
+
+ // Filter link.exe noise (msvc.cxx).
+ //
+ void
+ msvc_filter_link (ifdstream&, const file&, otype);
+
+ // Translate target CPU to the link.exe/lib.exe /MACHINE option.
+ //
+ const char*
+ msvc_machine (const string& cpu); // msvc.cxx
+
+ target_state link_rule::
+ perform_update (action a, const target& xt) const
+ {
+ tracer trace (x, "link_rule::perform_update");
+
+ const file& t (xt.as<file> ());
+ const path& tp (t.path ());
+
+ match_data& md (t.data<match_data> ());
+
+ // Unless the outer install rule signalled that this is update for
+ // install, signal back that we've performed plain update.
+ //
+ if (!md.for_install)
+ md.for_install = false;
+
+ bool for_install (*md.for_install);
+
+ const scope& bs (t.base_scope ());
+ const scope& rs (*bs.root_scope ());
+
+ ltype lt (link_type (t));
+ otype ot (lt.type);
+ linfo li (link_info (bs, ot));
+
+ // Update prerequisites. We determine if any relevant ones render us
+ // out-of-date manually below.
+ //
+ bool update (false);
+ timestamp mt (t.load_mtime ());
+ target_state ts (straight_execute_prerequisites (a, t));
+
+ // If targeting Windows, take care of the manifest.
+ //
+ path manifest; // Manifest itself (msvc) or compiled object file.
+ timestamp rpath_timestamp (timestamp_nonexistent); // DLLs timestamp.
+
+ if (lt.executable () && tclass == "windows")
+ {
+ // First determine if we need to add our rpath emulating assembly. The
+ // assembly itself is generated later, after updating the target. Omit
+ // it if we are updating for install.
+ //
+ if (!for_install)
+ rpath_timestamp = windows_rpath_timestamp (t, bs, a, li);
+
+ pair<path, bool> p (
+ windows_manifest (t,
+ rpath_timestamp != timestamp_nonexistent));
+
+ path& mf (p.first);
+ bool mf_cf (p.second); // Changed flag (timestamp resolution).
+
+ timestamp mf_mt (file_mtime (mf));
+
+ if (tsys == "mingw32")
+ {
+ // Compile the manifest into the object file with windres. While we
+ // are going to synthesize an .rc file to pipe to windres' stdin, we
+ // will still use .manifest to check if everything is up-to-date.
+ //
+ manifest = mf + ".o";
+
+ if (mf_mt > file_mtime (manifest) || mf_cf)
+ {
+ path of (relative (manifest));
+
+ const process_path& rc (cast<process_path> (rs["bin.rc.path"]));
+
+ // @@ Would be good to add this to depdb (e.g,, rc changes).
+ //
+ const char* args[] = {
+ rc.recall_string (),
+ "--input-format=rc",
+ "--output-format=coff",
+ "-o", of.string ().c_str (),
+ nullptr};
+
+ if (verb >= 3)
+ print_process (args);
+
+ try
+ {
+ process pr (rc, args, -1);
+
+ try
+ {
+ ofdstream os (move (pr.out_fd));
+
+ // 1 is resource ID, 24 is RT_MANIFEST. We also need to escape
+ // Windows path backslashes.
+ //
+ os << "1 24 \"";
+
+ const string& s (mf.string ());
+ for (size_t i (0), j;; i = j + 1)
+ {
+ j = s.find ('\\', i);
+ os.write (s.c_str () + i,
+ (j == string::npos ? s.size () : j) - i);
+
+ if (j == string::npos)
+ break;
+
+ os.write ("\\\\", 2);
+ }
+
+ os << "\"" << endl;
+
+ os.close ();
+ }
+ catch (const io_error& e)
+ {
+ if (pr.wait ()) // Ignore if child failed.
+ fail << "unable to pipe resource file to " << args[0]
+ << ": " << e;
+ }
+
+ run_finish (args, pr);
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << args[0] << ": " << e;
+
+ if (e.child)
+ exit (1);
+
+ throw failed ();
+ }
+
+ update = true; // Manifest changed, force update.
+ }
+ }
+ else
+ {
+ manifest = move (mf); // Save for link.exe's /MANIFESTINPUT.
+
+ if (mf_mt > mt || mf_cf)
+ update = true; // Manifest changed, force update.
+ }
+ }
+
+ // Check/update the dependency database.
+ //
+ depdb dd (tp + ".d");
+
+ // First should come the rule name/version.
+ //
+ if (dd.expect (rule_id) != nullptr)
+ l4 ([&]{trace << "rule mismatch forcing update of " << t;});
+
+ lookup ranlib;
+
+ // Then the linker checksum (ar/ranlib or the compiler).
+ //
+ if (lt.static_library ())
+ {
+ ranlib = rs["bin.ranlib.path"];
+
+ const char* rl (
+ ranlib
+ ? cast<string> (rs["bin.ranlib.checksum"]).c_str ()
+ : "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+
+ if (dd.expect (cast<string> (rs["bin.ar.checksum"])) != nullptr)
+ l4 ([&]{trace << "ar mismatch forcing update of " << t;});
+
+ if (dd.expect (rl) != nullptr)
+ l4 ([&]{trace << "ranlib mismatch forcing update of " << t;});
+ }
+ else
+ {
+ // For VC we use link.exe directly.
+ //
+ const string& cs (
+ cast<string> (
+ rs[tsys == "win32-msvc"
+ ? var_pool["bin.ld.checksum"]
+ : x_checksum]));
+
+ if (dd.expect (cs) != nullptr)
+ l4 ([&]{trace << "linker mismatch forcing update of " << t;});
+ }
+
+ // Next check the target. While it might be incorporated into the linker
+ // checksum, it also might not (e.g., VC link.exe).
+ //
+ if (dd.expect (ctgt.string ()) != nullptr)
+ l4 ([&]{trace << "target mismatch forcing update of " << t;});
+
+ // Start building the command line. While we don't yet know whether we
+ // will really need it, we need to hash it to find out. So the options
+ // are to either replicate the exact process twice, first for hashing
+ // then for building or to go ahead and start building and hash the
+ // result. The first approach is probably more efficient while the
+ // second is simpler. Let's got with the simpler for now (actually it's
+ // kind of a hybrid).
+ //
+ cstrings args {nullptr}; // Reserve one for config.bin.ar/config.x.
+
+ // Storage.
+ //
+ string soname1, soname2;
+ strings sargs;
+
+ if (lt.static_library ())
+ {
+ if (tsys == "win32-msvc") ;
+ else
+ {
+ // If the user asked for ranlib, don't try to do its function with
+ // -s. Some ar implementations (e.g., the LLVM one) don't support
+ // leading '-'.
+ //
+ args.push_back (ranlib ? "rc" : "rcs");
+ }
+ }
+ else
+ {
+ if (tsys == "win32-msvc")
+ {
+ // We are using link.exe directly so don't pass the compiler
+ // options.
+ }
+ else
+ {
+ append_options (args, t, c_coptions);
+ append_options (args, t, x_coptions);
+ append_options (args, tstd);
+ }
+
+ append_options (args, t, c_loptions);
+ append_options (args, t, x_loptions);
+
+ // Extra system library dirs (last).
+ //
+ // @@ /LIBPATH:<path>, not /LIBPATH <path>
+ //
+ assert (sys_lib_dirs_extra <= sys_lib_dirs.size ());
+ append_option_values (
+ args,
+ cclass == compiler_class::msvc ? "/LIBPATH:" : "-L",
+ sys_lib_dirs.begin () + sys_lib_dirs_extra, sys_lib_dirs.end (),
+ [] (const dir_path& d) {return d.string ().c_str ();});
+
+ // Handle soname/rpath.
+ //
+ if (tclass == "windows")
+ {
+ // Limited emulation for Windows with no support for user-defined
+ // rpaths.
+ //
+ auto l (t["bin.rpath"]);
+
+ if (l && !l->empty ())
+ fail << ctgt << " does not support rpath";
+ }
+ else
+ {
+ // Set soname.
+ //
+ if (lt.shared_library ())
+ {
+ const libs_paths& paths (md.libs_data);
+ const string& leaf (paths.effect_soname ().leaf ().string ());
+
+ if (tclass == "macos")
+ {
+ // With Mac OS 10.5 (Leopard) Apple finally caved in and gave us
+ // a way to emulate vanilla -rpath.
+ //
+ // It may seem natural to do something different on update for
+ // install. However, if we don't make it @rpath, then the user
+ // won't be able to use config.bin.rpath for installed libraries.
+ //
+ soname1 = "-install_name";
+ soname2 = "@rpath/" + leaf;
+ }
+ else
+ soname1 = "-Wl,-soname," + leaf;
+
+ if (!soname1.empty ())
+ args.push_back (soname1.c_str ());
+
+ if (!soname2.empty ())
+ args.push_back (soname2.c_str ());
+ }
+
+ // Add rpaths. We used to first add the ones specified by the user
+ // so that they take precedence. But that caused problems if we have
+ // old versions of the libraries sitting in the rpath location
+ // (e.g., installed libraries). And if you think about this, it's
+ // probably correct to prefer libraries that we explicitly imported
+ // to the ones found via rpath.
+ //
+ // Note also that if this is update for install, then we don't add
+ // rpath of the imported libraries (i.e., we assume they are also
+ // installed). But we add -rpath-link for some platforms.
+ //
+ rpath_libraries (sargs, t, bs, a, li, for_install);
+
+ if (auto l = t["bin.rpath"])
+ for (const dir_path& p: cast<dir_paths> (l))
+ sargs.push_back ("-Wl,-rpath," + p.string ());
+ }
+ }
+
+ // All the options should now be in. Hash them and compare with the db.
+ //
+ {
+ sha256 cs;
+
+ for (size_t i (1); i != args.size (); ++i)
+ cs.append (args[i]);
+
+ for (size_t i (0); i != sargs.size (); ++i)
+ cs.append (sargs[i]);
+
+ if (dd.expect (cs.string ()) != nullptr)
+ l4 ([&]{trace << "options mismatch forcing update of " << t;});
+ }
+
+ // Finally, hash and compare the list of input files.
+ //
+ // Should we capture actual files or their checksum? The only good
+ // reason for capturing actual files is diagnostics: we will be able
+ // to pinpoint exactly what is causing the update. On the other hand,
+ // the checksum is faster and simpler. And we like simple.
+ //
+ {
+ sha256 cs;
+
+ for (const prerequisite_target& p: t.prerequisite_targets[a])
+ {
+ const target* pt (p.target);
+
+ // If this is bmi*{}, then obj*{} is its ad hoc member.
+ //
+ if (modules)
+ {
+ if (pt->is_a<bmie> () || pt->is_a<bmia> () || pt->is_a<bmis> ())
+ pt = pt->member;
+ }
+
+ const file* f;
+ bool la (false), ls (false);
+
+ if ((f = pt->is_a<obje> ()) ||
+ (f = pt->is_a<obja> ()) ||
+ (f = pt->is_a<objs> ()) ||
+ (!lt.static_library () && // @@ UTL: TODO libua to liba link.
+ ((la = (f = pt->is_a<liba> ())) ||
+ (la = (f = pt->is_a<libux> ())) ||
+ (ls = (f = pt->is_a<libs> ())))))
+ {
+ // Link all the dependent interface libraries (shared) or interface
+ // and implementation (static), recursively.
+ //
+ // Also check if any of them render us out of date. The tricky
+ // case is, say, a utility library (static) that depends on a
+ // shared library. When the shared library is updated, there is no
+ // reason to re-archive the utility but those who link the utility
+ // have to "see through" the changes in the shared library.
+ //
+ if (la || ls)
+ {
+ hash_libraries (cs, update, mt, *f, la, p.data, bs, a, li);
+ f = nullptr; // Timestamp checked by hash_libraries().
+ }
+ else
+ cs.append (f->path ().string ());
+ }
+ else
+ f = pt->is_a<exe> (); // Consider executable mtime (e.g., linker).
+
+ // Check if this input renders us out of date.
+ //
+ if (f != nullptr)
+ update = update || f->newer (mt);
+ }
+
+ // Treat it as input for both MinGW and VC (mtime checked above).
+ //
+ if (!manifest.empty ())
+ cs.append (manifest.string ());
+
+ // Treat .libs as inputs, not options.
+ //
+ if (!lt.static_library ())
+ {
+ hash_options (cs, t, c_libs);
+ hash_options (cs, t, x_libs);
+ }
+
+ if (dd.expect (cs.string ()) != nullptr)
+ l4 ([&]{trace << "file set mismatch forcing update of " << t;});
+ }
+
+ // If any of the above checks resulted in a mismatch (different linker,
+ // options or input file set), or if the database is newer than the
+ // target (interrupted update) then force the target update. Also note
+ // this situation in the "from scratch" flag.
+ //
+ bool scratch (false);
+ if (dd.writing () || dd.mtime () > mt)
+ scratch = update = true;
+
+ dd.close ();
+
+ // (Re)generate pkg-config's .pc file. While the target itself might be
+ // up-to-date from a previous run, there is no guarantee that .pc exists
+ // or also up-to-date. So to keep things simple we just regenerate it
+ // unconditionally.
+ //
+ // Also, if you are wondering why don't we just always produce this .pc,
+ // install or no install, the reason is unless and until we are updating
+ // for install, we have no idea where-to things will be installed.
+ //
+ if (for_install)
+ {
+ bool la;
+ const file* f;
+
+ if ((la = (f = t.is_a<liba> ())) ||
+ ( f = t.is_a<libs> ()))
+ pkgconfig_save (a, *f, la);
+ }
+
+ // If nothing changed, then we are done.
+ //
+ if (!update)
+ return ts;
+
+ // Ok, so we are updating. Finish building the command line.
+ //
+ string out, out1, out2, out3; // Storage.
+
+ // Translate paths to relative (to working directory) ones. This results
+ // in easier to read diagnostics.
+ //
+ path relt (relative (tp));
+
+ const process_path* ld (nullptr);
+ if (lt.static_library ())
+ {
+ ld = &cast<process_path> (rs["bin.ar.path"]);
+
+ if (tsys == "win32-msvc")
+ {
+ // lib.exe has /LIBPATH but it's not clear/documented what it's used
+ // for. Perhaps for link-time code generation (/LTCG)? If that's the
+ // case, then we may need to pass *.loptions.
+ //
+ args.push_back ("/NOLOGO");
+
+ // Add /MACHINE.
+ //
+ args.push_back (msvc_machine (cast<string> (rs[x_target_cpu])));
+
+ out = "/OUT:" + relt.string ();
+ args.push_back (out.c_str ());
+ }
+ else
+ args.push_back (relt.string ().c_str ());
+ }
+ else
+ {
+ // The options are usually similar enough to handle executables
+ // and shared libraries together.
+ //
+ if (tsys == "win32-msvc")
+ {
+ // Using link.exe directly.
+ //
+ ld = &cast<process_path> (rs["bin.ld.path"]);
+ args.push_back ("/NOLOGO");
+
+ if (ot == otype::s)
+ args.push_back ("/DLL");
+
+ // Add /MACHINE.
+ //
+ args.push_back (msvc_machine (cast<string> (rs[x_target_cpu])));
+
+ // Unless explicitly enabled with /INCREMENTAL, disable incremental
+ // linking (it is implicitly enabled if /DEBUG is specified). The
+ // reason is the .ilk file: its name cannot be changed and if we
+ // have, say, foo.exe and foo.dll, then they will end up stomping on
+ // each other's .ilk's.
+ //
+ // So the idea is to disable it by default but let the user request
+ // it explicitly if they are sure their project doesn't suffer from
+ // the above issue. We can also have something like 'incremental'
+ // config initializer keyword for this.
+ //
+ // It might also be a good idea to ask Microsoft to add an option.
+ //
+ if (!find_option ("/INCREMENTAL", args, true))
+ args.push_back ("/INCREMENTAL:NO");
+
+ if (cid == compiler_id::clang)
+ {
+ // According to Clang's MSVC.cpp, we shall link libcmt.lib (static
+ // multi-threaded runtime) unless -nostdlib or -nostartfiles is
+ // specified.
+ //
+ if (!find_options ({"-nostdlib", "-nostartfiles"}, t, c_coptions) &&
+ !find_options ({"-nostdlib", "-nostartfiles"}, t, x_coptions))
+ args.push_back ("/DEFAULTLIB:libcmt.lib");
+ }
+
+ // If you look at the list of libraries Visual Studio links by
+ // default, it includes everything and a couple of kitchen sinks
+ // (winspool32.lib, ole32.lib, odbc32.lib, etc) while we want to
+ // keep our low-level build as pure as possible. However, there seem
+ // to be fairly essential libraries that are not linked by link.exe
+ // by default (use /VERBOSE:LIB to see the list). For example, MinGW
+ // by default links advapi32, shell32, user32, and kernel32. And so
+ // we follow suit and make sure those are linked. advapi32 and
+ // kernel32 are already on the default list and we only need to add
+ // the other two.
+ //
+ // The way we are going to do it is via the /DEFAULTLIB option
+ // rather than specifying the libraries as normal inputs (as VS
+ // does). This way the user can override our actions with the
+ // /NODEFAULTLIB option.
+ //
+ args.push_back ("/DEFAULTLIB:shell32.lib");
+ args.push_back ("/DEFAULTLIB:user32.lib");
+
+ // Take care of the manifest (will be empty for the DLL).
+ //
+ if (!manifest.empty ())
+ {
+ out3 = "/MANIFESTINPUT:";
+ out3 += relative (manifest).string ();
+ args.push_back ("/MANIFEST:EMBED");
+ args.push_back (out3.c_str ());
+ }
+
+ if (ot == otype::s)
+ {
+ // On Windows libs{} is the DLL and its first ad hoc group member
+ // is the import library.
+ //
+ // This will also create the .exp export file. Its name will be
+ // derived from the import library by changing the extension.
+ // Lucky for us -- there is no option to name it.
+ //
+ auto& imp (t.member->as<file> ());
+ out2 = "/IMPLIB:" + relative (imp.path ()).string ();
+ args.push_back (out2.c_str ());
+ }
+
+ // If we have /DEBUG then name the .pdb file. It is either the first
+ // (exe) or the second (dll) ad hoc group member.
+ //
+ if (find_option ("/DEBUG", args, true))
+ {
+ auto& pdb (
+ (ot == otype::e ? t.member : t.member->member)->as<file> ());
+ out1 = "/PDB:" + relative (pdb.path ()).string ();
+ args.push_back (out1.c_str ());
+ }
+
+ // @@ An executable can have an import library and VS seems to
+ // always name it. I wonder what would trigger its generation?
+ // Could it be the presence of export symbols? Yes, link.exe will
+ // generate the import library iff there are exported symbols.
+ // Which means there could be a DLL without an import library
+ // (which we currently don't handle very well).
+ //
+ out = "/OUT:" + relt.string ();
+ args.push_back (out.c_str ());
+ }
+ else
+ {
+ switch (cclass)
+ {
+ case compiler_class::gcc:
+ {
+ ld = &cpath;
+
+ // Add the option that triggers building a shared library and
+ // take care of any extras (e.g., import library).
+ //
+ if (ot == otype::s)
+ {
+ if (tclass == "macos")
+ args.push_back ("-dynamiclib");
+ else
+ args.push_back ("-shared");
+
+ if (tsys == "mingw32")
+ {
+ // On Windows libs{} is the DLL and its first ad hoc group
+ // member is the import library.
+ //
+ auto& imp (t.member->as<file> ());
+ out = "-Wl,--out-implib=" + relative (imp.path ()).string ();
+ args.push_back (out.c_str ());
+ }
+ }
+
+ args.push_back ("-o");
+ args.push_back (relt.string ().c_str ());
+ break;
+ }
+ case compiler_class::msvc: assert (false);
+ }
+ }
+ }
+
+ args[0] = ld->recall_string ();
+
+ // The same logic as during hashing above.
+ //
+ for (const prerequisite_target& p: t.prerequisite_targets[a])
+ {
+ const target* pt (p.target);
+
+ if (modules)
+ {
+ if (pt->is_a<bmie> () || pt->is_a<bmia> () || pt->is_a<bmis> ())
+ pt = pt->member;
+ }
+
+ const file* f;
+ bool la (false), ls (false);
+
+ if ((f = pt->is_a<obje> ()) ||
+ (f = pt->is_a<obja> ()) ||
+ (f = pt->is_a<objs> ()) ||
+ (!lt.static_library () && // @@ UTL: TODO libua to liba link.
+ ((la = (f = pt->is_a<liba> ())) ||
+ (la = (f = pt->is_a<libux> ())) ||
+ (ls = (f = pt->is_a<libs> ())))))
+ {
+ // Link all the dependent interface libraries (shared) or interface
+ // and implementation (static), recursively.
+ //
+ if (la || ls)
+ append_libraries (sargs, *f, la, p.data, bs, a, li);
+ else
+ sargs.push_back (relative (f->path ()).string ()); // string()&&
+ }
+ }
+
+ // For MinGW manifest is an object file.
+ //
+ if (!manifest.empty () && tsys == "mingw32")
+ sargs.push_back (relative (manifest).string ());
+
+ // Shallow-copy sargs to args. Why not do it as we go along pushing into
+ // sargs? Because of potential reallocations.
+ //
+ for (const string& a: sargs)
+ args.push_back (a.c_str ());
+
+ if (!lt.static_library ())
+ {
+ append_options (args, t, c_libs);
+ append_options (args, t, x_libs);
+ }
+
+ args.push_back (nullptr);
+
+ // Cleanup old (versioned) libraries.
+ //
+ if (lt.shared_library ())
+ {
+ const libs_paths& paths (md.libs_data);
+ const path& p (paths.clean);
+
+ if (!p.empty ())
+ try
+ {
+ if (verb >= 4) // Seeing this with -V doesn't really add any value.
+ text << "rm " << p;
+
+ auto rm = [&paths, this] (path&& m, const string&, bool interm)
+ {
+ if (!interm)
+ {
+ // Filter out paths that have one of the current paths as a
+ // prefix.
+ //
+ auto test = [&m] (const path& p)
+ {
+ const string& s (p.string ());
+ return s.empty () || m.string ().compare (0, s.size (), s) != 0;
+ };
+
+ if (test (*paths.real) &&
+ test (paths.interm) &&
+ test (paths.soname) &&
+ test (paths.link))
+ {
+ try_rmfile (m);
+ try_rmfile (m + ".d");
+
+ if (tsys == "win32-msvc")
+ {
+ try_rmfile (m.base () += ".ilk");
+ try_rmfile (m += ".pdb");
+ }
+ }
+ }
+ return true;
+ };
+
+ path_search (p, rm, dir_path (), false); // Don't follow symlinks.
+ }
+ catch (const system_error&) {} // Ignore errors.
+ }
+ else if (lt.static_library ())
+ {
+ // We use relative paths to the object files which means we may end
+ // up with different ones depending on CWD and some implementation
+ // treat them as different archive members. So remote the file to
+ // be sure. Note that we ignore errors leaving it to the achiever
+ // to complain.
+ //
+ if (mt != timestamp_nonexistent)
+ try_rmfile (relt, true);
+ }
+
+ if (verb >= 2)
+ print_process (args);
+ else if (verb)
+ text << "ld " << t;
+
+ try
+ {
+ // VC tools (both lib.exe and link.exe) send diagnostics to stdout.
+ // Also, link.exe likes to print various gratuitous messages. So for
+ // link.exe we redirect stdout to a pipe, filter that noise out, and
+ // send the rest to stderr.
+ //
+ // For lib.exe (and any other insane compiler that may try to pull off
+ // something like this) we are going to redirect stdout to stderr. For
+ // sane compilers this should be harmless.
+ //
+ bool filter (tsys == "win32-msvc" && !lt.static_library ());
+
+ process pr (*ld, args.data (), 0, (filter ? -1 : 2));
+
+ if (filter)
+ {
+ try
+ {
+ ifdstream is (
+ move (pr.in_ofd), fdstream_mode::text, ifdstream::badbit);
+
+ msvc_filter_link (is, t, ot);
+
+ // If anything remains in the stream, send it all to stderr. Note
+ // that the eof check is important: if the stream is at eof, this
+ // and all subsequent writes to the diagnostics stream will fail
+ // (and you won't see a thing).
+ //
+ if (is.peek () != ifdstream::traits_type::eof ())
+ diag_stream_lock () << is.rdbuf ();
+
+ is.close ();
+ }
+ catch (const io_error&) {} // Assume exits with error.
+ }
+
+ run_finish (args, pr);
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << args[0] << ": " << e;
+
+ // In a multi-threaded program that fork()'ed but did not exec(),
+ // it is unwise to try to do any kind of cleanup (like unwinding
+ // the stack and running destructors).
+ //
+ if (e.child)
+ exit (1);
+
+ throw failed ();
+ }
+
+ // Remove the target file if any of the subsequent actions fail. If we
+ // don't do that, we will end up with a broken build that is up-to-date.
+ //
+ auto_rmfile rm (relt);
+
+ if (ranlib)
+ {
+ const process_path& rl (cast<process_path> (ranlib));
+
+ const char* args[] = {
+ rl.recall_string (),
+ relt.string ().c_str (),
+ nullptr};
+
+ if (verb >= 2)
+ print_process (args);
+
+ run (rl, args);
+ }
+
+ if (tclass == "windows")
+ {
+ // For Windows generate rpath-emulating assembly (unless updating for
+ // install).
+ //
+ if (lt.executable () && !for_install)
+ windows_rpath_assembly (t, bs, a, li,
+ cast<string> (rs[x_target_cpu]),
+ rpath_timestamp,
+ scratch);
+ }
+ else if (lt.shared_library ())
+ {
+ // For shared libraries we may need to create a bunch of symlinks.
+ //
+ auto ln = [] (const path& f, const path& l)
+ {
+ if (verb >= 3)
+ text << "ln -sf " << f << ' ' << l;
+
+ try
+ {
+ if (file_exists (l, false)) // The -f part.
+ try_rmfile (l);
+
+ mksymlink (f, l);
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to create symlink " << l << ": " << e;
+ }
+ };
+
+ const libs_paths& paths (md.libs_data);
+
+ const path& lk (paths.link);
+ const path& so (paths.soname);
+ const path& in (paths.interm);
+
+ const path* f (paths.real);
+
+ if (!in.empty ()) {ln (f->leaf (), in); f = &in;}
+ if (!so.empty ()) {ln (f->leaf (), so); f = &so;}
+ if (!lk.empty ()) {ln (f->leaf (), lk);}
+ }
+
+ rm.cancel ();
+
+ // Should we go to the filesystem and get the new mtime? We know the
+ // file has been modified, so instead just use the current clock time.
+ // It has the advantage of having the subseconds precision.
+ //
+ t.mtime (system_clock::now ());
+ return target_state::changed;
+ }
+
+ target_state link_rule::
+ perform_clean (action a, const target& xt) const
+ {
+ const file& t (xt.as<file> ());
+ ltype lt (link_type (t));
+
+ if (lt.executable ())
+ {
+ if (tclass == "windows")
+ {
+ if (tsys == "mingw32")
+ return clean_extra (
+ a, t, {".d", ".dlls/", ".manifest.o", ".manifest"});
+ else
+ // Assuming it's VC or alike. Clean up .ilk in case the user
+ // enabled incremental linking (note that .ilk replaces .exe).
+ //
+ return clean_extra (
+ a, t, {".d", ".dlls/", ".manifest", "-.ilk"});
+ }
+ }
+ else if (lt.shared_library ())
+ {
+ if (tclass == "windows")
+ {
+ // Assuming it's VC or alike. Clean up .exp and .ilk.
+ //
+ // Note that .exp is based on the .lib, not .dll name. And with
+ // versioning their bases may not be the same.
+ //
+ if (tsys != "mingw32")
+ return clean_extra (a, t, {{".d", "-.ilk"}, {"-.exp"}});
+ }
+ else
+ {
+ // Here we can have a bunch of symlinks that we need to remove. If
+ // the paths are empty, then they will be ignored.
+ //
+ const libs_paths& paths (t.data<match_data> ().libs_data);
+
+ return clean_extra (a, t, {".d",
+ paths.link.string ().c_str (),
+ paths.soname.string ().c_str (),
+ paths.interm.string ().c_str ()});
+ }
+ }
+ // For static library it's just the defaults.
+
+ return clean_extra (a, t, {".d"});
+ }
+ }
+}