// file      : libbuild2/cc/link-rule.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <libbuild2/cc/link-rule.hxx>

#include <map>
#include <cstdlib>  // exit()
#include <cstring>  // strlen()

#include <libbutl/filesystem.mxx> // file_exists(), path_search()

#include <libbuild2/depdb.hxx>
#include <libbuild2/scope.hxx>
#include <libbuild2/context.hxx>
#include <libbuild2/variable.hxx>
#include <libbuild2/algorithm.hxx>
#include <libbuild2/filesystem.hxx>
#include <libbuild2/diagnostics.hxx>

#include <libbuild2/bin/rule.hxx>    // lib_rule::build_members()
#include <libbuild2/bin/target.hxx>
#include <libbuild2/bin/utility.hxx>

#include <libbuild2/cc/target.hxx>  // c, pc*
#include <libbuild2/cc/utility.hxx>

using std::map;
using std::exit;

using namespace butl;

namespace build2
{
  namespace cc
  {
    using namespace bin;

    link_rule::
    link_rule (data&& d)
        : common (move (d)),
          rule_id (string (x) += ".link 2")
    {
      static_assert (sizeof (match_data) <= target::data_size,
                     "insufficient space");
    }

    link_rule::match_result link_rule::
    match (action a,
           const target& t,
           const target* g,
           otype ot,
           bool library) const
    {
      // NOTE: the target may be a group (see utility library logic below).

      match_result r;

      // Scan prerequisites and see if we can work with what we've got. Note
      // that X could be C (as in language). We handle this by always checking
      // for X first.
      //
      // Note also that we treat bmi{} as obj{}. @@ MODHDR hbmi{}?
      //
      for (prerequisite_member p:
             prerequisite_members (a, t, group_prerequisites (t, g)))
      {
        // If excluded or ad hoc, then don't factor it into our tests.
        //
        if (include (a, t, p) != include_type::normal)
          continue;

        if (p.is_a (x_src)                        ||
            (x_mod != nullptr && p.is_a (*x_mod)) ||
            // Header-only X library (or library with C source and X header).
            (library          && x_header (p, false /* c_hdr */)))
        {
          r.seen_x = r.seen_x || true;
        }
        else if (p.is_a<c> ()            ||
                 // Header-only C library.
                 (library && p.is_a<h> ()))
        {
          r.seen_c = r.seen_c || true;
        }
        else if (p.is_a<obj> () || p.is_a<bmi> ())
        {
          r.seen_obj = r.seen_obj || true;
        }
        else if (p.is_a<obje> () || p.is_a<bmie> ())
        {
          // We can make these "no-match" if/when there is a valid use case.
          //
          if (ot != otype::e)
            fail << p.type ().name << "{} as prerequisite of " << t;

          r.seen_obj = r.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;

          r.seen_obj = r.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;

          r.seen_obj = r.seen_obj || true;
        }
        else if (p.is_a<libul> () || p.is_a<libux> ())
        {
          // For a unility library we look at its prerequisites, recursively.
          // Since these checks are not exactly light-weight, only do them if
          // we haven't already seen any X prerequisites.
          //
          if (!r.seen_x)
          {
            // This is a bit iffy: in our model a rule can only search a
            // target's prerequisites if it matches. But we don't yet know
            // whether we match. However, it seems correct to assume that any
            // rule-specific search will always resolve to an existing target
            // if there is one. So perhaps it's time to relax this restriction
            // a little? Note that this fits particularly well with what we
            // doing here since if there is no existing target, then there can
            // be no prerequisites.
            //
            // Note, however, that we cannot link-up a prerequisite target
            // member to its group since we are not matching this target. As
            // result we have to do all the steps except for setting t.group
            // and pass both member and group (we also cannot query t.group
            // since it's racy).
            //
            const target* pg (nullptr);
            const target* pt (p.search_existing ());

            if (p.is_a<libul> ())
            {
              if (pt != nullptr)
              {
                // If this is a group then try to pick (again, if exists) a
                // suitable member. If it doesn't exist, then we will only be
                // considering the group's prerequisites.
                //
                if (const target* pm =
                    link_member (pt->as<libul> (),
                                 a,
                                 linfo {ot, lorder::a /* unused */},
                                 true /* existing */))
                {
                  pg = pt;
                  pt = pm;
                }
              }
              else
              {
                // It's possible we have no group but have a member so try
                // that.
                //
                const target_type& tt (ot == otype::a ? libua::static_type :
                                       ot == otype::s ? libus::static_type :
                                       libue::static_type);

                // We know this prerequisite member is a prerequisite since
                // otherwise the above search would have returned the member
                // target.
                //
                pt = search_existing (t.ctx, p.prerequisite.key (tt));
              }
            }
            else if (!p.is_a<libue> ())
            {
              // See if we also/instead have a group.
              //
              pg = search_existing (t.ctx,
                                    p.prerequisite.key (libul::static_type));

              if (pt == nullptr)
                swap (pt, pg);
            }

            if (pt != nullptr)
            {
              // If we are matching a target, use the original output type
              // since that would be the member that we pick.
              //
              otype pot (pt->is_a<libul> () ? ot : link_type (*pt).type);
              match_result pr (match (a, *pt, pg, pot, true /* lib */));

              // Do we need to propagate any other seen_* values? Hm, that
              // would in fact match with the "see-through" semantics of
              // utility libraries we have in other places.
              //
              r.seen_x = pr.seen_x;
            }
            else
              r.seen_lib = r.seen_lib || true; // Consider as just a library.
          }
        }
        else if (p.is_a<lib> ()  ||
                 p.is_a<liba> () ||
                 p.is_a<libs> ())
        {
          r.seen_lib = r.seen_lib || true;
        }
        // Some other c-common header/source (say C++ in a C rule) other than
        // a C header (we assume everyone can hanle that).
        //
        else if (p.is_a<cc> () && !(x_header (p, true /* c_hdr */)))
        {
          r.seen_cc = true;
          break;
        }
      }

      return r;
    }

    bool link_rule::
    match (action a, target& t, const string& hint) const
    {
      // NOTE: may be called multiple times and for both inner and outer
      //       operations (see the install rules).

      tracer trace (x, "link_rule::match");

      ltype lt (link_type (t));

      // If this is a group member library, link-up to our group (this is the
      // target group protocol which means this can be done whether we match
      // or not).
      //
      // If we are called for the outer operation (see install rules), then
      // use resolve_group() to delegate to inner.
      //
      if (lt.member_library ())
      {
        if (a.outer ())
          resolve_group (a, t);
        else if (t.group == nullptr)
          t.group = &search (t,
                             lt.utility ? libul::static_type : lib::static_type,
                             t.dir, t.out, t.name);
      }

      match_result r (match (a, t, t.group, lt.type, lt.library ()));

      // If this is some other c-common header/source (say C++ in a C rule),
      // then we shouldn't try to handle that (it may need to be compiled,
      // etc).
      //
      if (r.seen_cc)
      {
        l4 ([&]{trace << "non-" << x_lang << " prerequisite "
                      << "for target " << t;});
        return false;
      }

      if (!(r.seen_x || r.seen_c || r.seen_obj || r.seen_lib))
      {
        l4 ([&]{trace << "no " << x_lang << ", C, or obj/lib prerequisite "
                      << "for target " << t;});
        return false;
      }

      // We will only chain a C source if there is also an X source or we were
      // explicitly told to.
      //
      if (r.seen_c && !r.seen_x && hint < x)
      {
        l4 ([&]{trace << "C prerequisite without " << x_lang << " or hint "
                      << "for target " << t;});
        return false;
      }

      return true;
    }

    auto link_rule::
    derive_libs_paths (file& t,
                       const char* pfx,
                       const char* sfx) const -> libs_paths
    {
      bool win (tclass == "windows");

      // Get default prefix and extension.
      //
      const char* ext (nullptr);
      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 (t.derive_extension (ext));

      auto append_ext = [&e] (path& p)
      {
        if (!e.empty ())
        {
          p += '.';
          p += e;
        }
      };

      // See if we have the load suffix.
      //
      const string& ls (cast_empty<string> (t["bin.lib.load_suffix"]));

      // Figure out the version.
      //
      string ver;
      bool verp (true); // Platform-specific.
      using verion_map = map<string, string>;
      if (const verion_map* m = cast_null<verion_map> (t["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 ("*");

        // Finally look for the platform-independent version.
        //
        if (i == m->end ())
        {
          verp = false;
          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>";

        ver = i->second;
      }

      // Now determine the paths.
      //
      path lk, ld, so, in;

      // We start with the basic path.
      //
      path b (t.dir);

      if (pfx != nullptr && pfx[0] != '\0')
      {
        b /= pfx;
        b += t.name;
      }
      else
        b /= t.name;

      if (sfx != nullptr && sfx[0] != '\0')
        b += sfx;

      // Clean patterns.
      //
      // Note that looser patterns tend to match all kinds of unexpected
      // stuff, for example (using Windows; without the lib prefix things are
      // even worse):
      //
      // foo-io.dll
      // foo.dll.obj
      // foo-1.dll.obj
      // foo.dll.u.lib
      //
      // Even with these patterns we tighted things up we do additional
      // filtering (of things like .d, .t that derived from the suffixed
      // and versioned name) at the match site.
      //
      path cp_l, cp_v;

      // Append separator characters (`-`, `_`, maybe `-v`) to the clean
      // pattern until we encounter a digit. Return false if the digit was
      // never encountered.
      //
      auto append_sep = [] (path& cp, const string& s) -> bool
      {
        for (char c: s)
        {
          if (digit (c))
            return true;

          cp += c;
        }

        return false;
      };

      // On Windows the real path is to libs{} and the link path is empty.
      // Note that we still need to derive the import library path.
      //
      if (win)
      {
        // Usually on Windows with MSVC 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.
        //
        libi& i (*find_adhoc_member<libi> (t));

        if (i.path ().empty ())
        {
          path ip (b);
          append_ext (ip);
          i.derive_path (move (ip), tsys == "mingw32" ? "a" : "lib");
        }
      }
      // We will only need the link name if the following name differs.
      //
      else if (!ver.empty () || !ls.empty ())
      {
        lk = b;
        append_ext (lk);
      }

      // See if we have the load suffix.
      //
      if (!ls.empty ())
      {
        // Derive the load suffix clean pattern (e.g., `foo-[0-9]*.dll`).
        //
        // Note: postpone appending the extension since we use this pattern as
        // a base for the version clean pattern.
        //
        cp_l = b;
        if (auto* p = cast_null<string> (t["bin.lib.load_suffix_pattern"]))
          cp_l += *p;
        else if (append_sep (cp_l, ls))
          cp_l += "[0-9]*";
        else
          cp_l.clear (); // Non-digit load suffix (use custom clean pattern).

        b += ls;

        // We will only need the load name if the following name differs.
        //
        if (!ver.empty ())
        {
          ld = b;
          append_ext (ld);
        }
      }

      // Append version and derive the real name.
      //
      const path* re (nullptr);
      if (ver.empty () || !verp)
      {
        if (!ver.empty ())
        {
          // Derive the version clean pattern (e.g., `foo-[0-9]*.dll`, or, if
          // we have the load clean pattern, `foo-[0-9]*-[0-9]*.dll`).
          //
          cp_v = cp_l.empty () ? b : cp_l;

          if (auto* p = cast_null<string> (t["bin.lib.version_pattern"]))
            cp_v += *p;
          else if (append_sep (cp_v, ver))
            cp_v += "[0-9]*";
          else
            cp_v.clear (); // Non-digit version (use custom clean pattern).

          if (!cp_v.empty ())
            append_ext (cp_v);

          b += ver;
        }

        re = &t.derive_path (move (b));
      }
      else
      {
        // Derive the version clean pattern (e.g., `libfoo.so.[0-9]*`, or, if
        // we have the load clean pattern, `libfoo-[0-9]*.so.[0-9]*`).
        //
        cp_v = cp_l.empty () ? b : cp_l;
        append_ext (cp_v);
        cp_v += ".[0-9]*";

        // Parse the next version component in the X.Y.Z version form.
        //
        // Note that we don't bother verifying components are numeric assuming
        // the user knows what they are doing (one can sometimes see versions
        // with non-numeric components though probably not for X).
        //
        auto next = [&ver,
                     b = size_t (0),
                     e = size_t (0)] (const char* what = nullptr) mutable
        {
          if (size_t n = next_word (ver, b, e, '.'))
            return string (ver, b, n);

          if (what != nullptr)
            fail << "missing " << what << " in shared library version '"
                 << ver << "'" << endf;

          return string ();
        };

        if (tclass == "linux")
        {
          // On Linux the shared library version has the MAJOR.MINOR[.EXTRA]
          // form where MAJOR is incremented for backwards-incompatible ABI
          // changes, MINOR -- for backwards-compatible, and optional EXTRA
          // has no specific meaning and can be used as some sort of release
          // or sequence number (e.g., if the ABI has not changed).
          //
          string ma (next ("major component"));
          string mi (next ("minor component"));
          string ex (next ());

          // The SONAME is libfoo.so.MAJOR
          //
          so = b;
          append_ext (so);
          so += '.'; so += ma;

          // If we have EXTRA, then make libfoo.so.MAJOR.MINOR to be the
          // intermediate name.
          //
          if (!ex.empty ())
          {
            in = b;
            append_ext (in);
            in += '.'; in += ma;
            in += '.'; in += mi;
          }

          // Add the whole version as the extra extension(s).
          //
          re = &t.derive_path (move (b),
                               nullptr      /* default_ext */,
                               ver.c_str () /* extra_ext */);
        }
        else
          fail << tclass << "-specific bin.lib.version not yet supported";
      }

      if (!cp_l.empty ()) append_ext (cp_l);

      return libs_paths {
        move (lk),
        move (ld),
        move (so),
        move (in),
        re,
        move (cp_l), move (cp_v)};
    }

    // Look for binary-full utility library recursively until we hit a
    // non-utility "barier".
    //
    static bool
    find_binfull (action a, const target& t, linfo li)
    {
      for (const target* pt: t.prerequisite_targets[a])
      {
        if (pt == nullptr || unmark (pt) != 0) // Called after pass 1 below.
          continue;

        const file* pf;

        // If this is the libu*{} group, then pick the appropriate member.
        //
        if (const libul* ul = pt->is_a<libul> ())
        {
          pf = &link_member (*ul, a, li)->as<file> ();
        }
        else if ((pf = pt->is_a<libue> ()) ||
                 (pf = pt->is_a<libus> ()) ||
                 (pf = pt->is_a<libua> ()))
          ;
        else
          continue;

        if (!pf->path ().empty () || find_binfull (a, *pf, li))
          return true;
      }

      return false;
    };

    recipe link_rule::
    apply (action a, target& xt) const
    {
      tracer trace (x, "link_rule::apply");

      file& t (xt.as<file> ());
      context& ctx (t.ctx);

      // 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) as rule-specific variable.
      //
      if (lt.library ())
        t.state[a].assign (c_type) = string (x);

      bool binless (lt.library ()); // Binary-less until proven otherwise.

      // Inject dependency on the output directory. Note that we do it even
      // for binless libraries since there could be other output (e.g., .pc
      // files).
      //
      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.
      //
      // Also clear the binless flag if we see any source or object files.
      // Note that if we don't see any this still doesn't mean the library is
      // binless since it can depend on a binfull utility library. This we
      // check below, after matching the libraries.
      //
      // 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. Except for libraries: if we
      // ignore them, then they won't be added to synthesized dependencies and
      // this will break things if we do, say, update after clean in the same
      // invocation. So for libraries we ignore them later, on pass 3.
      //
      optional<dir_paths> usr_lib_dirs; // Extract lazily.
      compile_target_types tts (compile_types (ot));

      auto skip = [&a, &rs] (const target* pt) -> bool
      {
        return a.operation () == clean_id && !pt->dir.sub (rs.out_path ());
      };

      auto& pts (t.prerequisite_targets[a]);
      size_t start (pts.size ());

      for (prerequisite_member p: group_prerequisite_members (a, t))
      {
        include_type pi (include (a, t, p));

        // We pre-allocate a NULL slot for each (potential; see clean)
        // prerequisite target.
        //
        pts.push_back (prerequisite_target (nullptr, pi));
        const target*& pt (pts.back ());

        if (pi != include_type::normal) // Skip excluded and ad hoc.
          continue;

        // Mark:
        //   0 - lib
        //   1 - src
        //   2 - mod
        //   3 - obj/bmi and also lib not to be cleaned
        //
        uint8_t m (0);

        bool mod (x_mod != nullptr && p.is_a (*x_mod));

        if (mod || p.is_a (x_src) || p.is_a<c> ())
        {
          binless = binless && false;

          // 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 : tts.bmi)
                                  : (group ? obj::static_type : tts.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))
          {
            pt = nullptr;
            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))
            m = 3; // Mark so it is not matched.

          // 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, tts.obj, p.key ());
          else if (p.is_a<bmi> ()) pt = &search (t, tts.bmi, p.key ());
          //
          // Windows module definition (.def). For other platforms (and for
          // static libraries) treat it as an ordinary prerequisite.
          //
          else if (p.is_a<def> () && tclass == "windows" && ot != otype::a)
          {
            pt = &p.search (t);
          }
          //
          // Something else. This could be something unrelated that the user
          // tacked on (e.g., a doc{}). Or it could be some ad hoc input to
          // the linker (say a linker script or some such).
          //
          else
          {
            if (!p.is_a<objx> () && !p.is_a<bmix> ())
            {
              // @@ Temporary hack until we get the default outer operation
              // for update. This allows operations like test and install to
              // skip such tacked on stuff.
              //
              // Note that ad hoc inputs have to be explicitly marked with the
              // include=adhoc prerequisite-specific variable.
              //
              if (ctx.current_outer_oif != nullptr)
                continue;
            }

            pt = &p.search (t);
          }

          if (skip (pt))
          {
            pt = nullptr;
            continue;
          }

          // @@ MODHDR: hbmix{} has no objx{}
          //
          binless = binless && !(pt->is_a<objx> () || pt->is_a<bmix> ());

          m = 3;
        }

        mark (pt, m);
      }

      // Match lib{} (the only unmarked) in parallel and wait for completion.
      //
      match_members (a, t, pts, start);

      // Check if we have any binfull utility libraries.
      //
      binless = binless && !find_binfull (a, t, li);

      // Now that we know for sure whether we are binless, derive file name(s)
      // and add ad hoc group members. Note that for binless we still need the
      // .pc member (whose name depends on the libray prefix) so we take care
      // to not derive the path for the library target itself inside.
      //
      {
        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;
              }
            }
          }

          if (binless)
            t.path (empty_path);
          else
            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";
              }

              if (binless)
                t.path (empty_path);
              else
                t.derive_path (e, p, s);

              break;
            }
          case otype::s:
            {
              if (binless)
                t.path (empty_path);
              else
              {
                // 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")
                {
                  e = "dll";
                  add_adhoc_member<libi> (t);
                }

                md.libs_paths = derive_libs_paths (t, p, s);
              }

              break;
            }
          }

          // Add VC's .pdb. Note that we are looking for the link.exe /DEBUG
          // option.
          //
          if (!binless && ot != otype::a && tsys == "win32-msvc")
          {
            if (find_option ("/DEBUG", t, c_loptions, true) ||
                find_option ("/DEBUG", t, x_loptions, true))
            {
              const target_type& tt (*bs.find_target_type ("pdb"));

              // We call the target foo.{exe,dll}.pdb rather than just foo.pdb
              // because we can have both foo.exe and foo.dll in the same
              // directory.
              //
              file& pdb (add_adhoc_member<file> (t, tt, e));

              // Note that the path is derived from the exe/dll path (so it
              // will include the version in case of a dll).
              //
              if (pdb.path ().empty ())
                pdb.derive_path (t.path (), "pdb");
            }
          }

          // Add pkg-config's .pc file.
          //
          // Note that we do it regardless of whether we are installing or not
          // for two reasons. Firstly, it is not easy to detect this situation
          // here since the for_install hasn't yet been communicated by
          // install_rule. Secondly, always having this member takes care of
          // cleanup automagically. The actual generation happens in
          // perform_update() below.
          //
          // Things are even trickier for the common .pc file: we only want to
          // have it in the shared library if we are not installing static
          // (see pkgconfig_save() for details). But we can't know it at this
          // stage. So what we are going to do is conceptually tie the common
          // file to the lib{} group (which does somehow feel correct) by only
          // installing it if the lib{} group is installed. Specifically, here
          // we will use its bin.lib to decide what will be installed and in
          // perform_update() we will confirm that it is actually installed.
          //
          if (ot != otype::e)
          {
            // 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 refer to the library. This
            // is also the reason why we use the .static and .shared second-
            // level extensions rather that a./.lib and .so/.dylib/.dll.

            // Note also that the order in which we are adding these members
            // is important (see add_addhoc_member() for details).
            //
            if (ot == otype::a || !lib_rule::build_members (rs).a)
            {
              auto& pc (add_adhoc_member<pc> (t));

              if (pc.path ().empty ())
                pc.derive_path (nullptr, (p == nullptr ? "lib" : p), s);
            }

            auto& pcx (add_adhoc_member<file> (t,
                                               (ot == otype::a
                                                ? pca::static_type
                                                : pcs::static_type)));

            if (pcx.path ().empty ())
              pcx.derive_path (nullptr, (p == nullptr ? "lib" : p), s);
          }

          // Add the Windows rpath emulating assembly directory as fsdir{}.
          //
          // Currently this is used in the backlinking logic and in the future
          // could also be used for clean (though there we may want to clean
          // old assemblies).
          //
          if (ot == otype::e && tclass == "windows")
          {
            // Note that here we cannot determine whether we will actually
            // need one (for_install, library timestamps are not available at
            // this point to call windows_rpath_timestamp()). So we may add
            // the ad hoc target but actually not produce the assembly. So
            // whomever relies on this must check if the directory actually
            // exists (windows_rpath_assembly() does take care to clean it up
            // if not used).
            //
#ifdef _WIN32
            target& dir =
#endif
              add_adhoc_member (t,
                                fsdir::static_type,
                                path_cast<dir_path> (t.path () + ".dlls"),
                                t.out,
                                string () /* name */);

            // By default our backlinking logic will try to symlink the
            // directory and it can even be done on Windows using junctions.
            // The problem is the Windows DLL assembly "logic" refuses to
            // recognize a junction as a valid assembly for some reason. So we
            // are going to resort to copy-link (i.e., a real directory with a
            // bunch of links). Note also that while DLLs can be symlinked,
            // the assembly manifest cannot (has to be hard-linked or copied).
            //
            // Interestingly, the directory symlink works just fine under
            // Wine. So we only resort to copy-link'ing if we are running on
            // Windows.
            //
#ifdef _WIN32
            dir.state[a].assign (ctx.var_backlink) = "copy";
#endif
          }
        }
      }

      // 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 prerequisites/prerequisite_targets loop.
      //
      size_t i (start);
      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;

        // New mark:
        //  1 - completion
        //  2 - verification
        //
        uint8_t m (unmark (pt));

        if (m == 3)                // obj/bmi or lib not to be cleaned
        {
          m = 1; // Just completion.

          // Note that if this is a library not to be cleaned, we keep it
          // marked for completion (see the next phase).
        }
        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 ? tts.bmi : tts.obj), rt.dir, rt.out, rt.name)
            : &rt;

          const target_type& rtt (mod
                                  ? (group ? bmi::static_type : tts.bmi)
                                  : (group ? obj::static_type : tts.obj));

          // 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 us to it. In this case also verify.
          //
          bool verify (true);

          // Note that we cannot use has_group_prerequisites() since the
          // target is not yet matched. So we check the group directly. Of
          // course, all of this is racy (see below).
          //
          if (!pt->has_prerequisites () &&
              (!group || !rt.has_prerequisites ()))
          {
            prerequisites ps {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 (pt == nullptr) // Note: ad hoc is taken care of.
                continue;

              // NOTE: pt may be marked (even for a library -- see clean
              // above). So watch out for a faux pax in this careful dance.
              //
              if (p.is_a<libx> () ||
                  p.is_a<liba> () || p.is_a<libs> () || p.is_a<libux> () ||
                  p.is_a<bmi> ()  || p.is_a (tts.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: adding to the group, not the member.
            //
            verify = !rt.prerequisites (move (ps));

            // Recheck that the target still has no prerequisites. If that's
            // no longer the case, then verify the result is compatible with
            // what we need.
            //
            // Note that there are scenarios where we will not detect this or
            // the detection will be racy. For example, thread 1 adds the
            // prerequisite to the group and then thread 2, which doesn't use
            // the group, adds the prerequisite to the member. This could be
            // triggered by something like this (undetectable):
            //
            // lib{foo}: cxx{foo}
            // exe{foo}: cxx{foo}
            //
            // Or this (detection is racy):
            //
            // lib{bar}: cxx{foo}
            // liba{baz}: cxx{foo}
            //
            // The current feeling, however, is that in non-contrived cases
            // (i.e., the source file is the same) this should be harmless.
            //
            if (!verify && group)
              verify = pt->has_prerequisites ();
          }

          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.
            //
            resolve_group (a, *pt); // Not matched yet so resolve group.

            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<bmix> ()                      ||
                  (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 (ctx.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->lookup_original (var, true).first;

            if (!l.defined ())
            {
              bool g (pt->group != nullptr);
              l = bs.lookup_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 (ctx, ctx.count_busy (), t[a].task_count, true);

      i = start;
      for (prerequisite_member p: group_prerequisite_members (a, t))
      {
        bool adhoc (pts[i].adhoc);
        const target*& pt (pts[i++]);

        uint8_t m;

        if (pt == nullptr)
        {
          // Handle ad hoc prerequisities.
          //
          if (!adhoc)
            continue;

          pt = &p.search (t);
          m = 1; // Mark for completion.
        }
        else if ((m = unmark (pt)) != 0)
        {
          // If this is a library not to be cleaned, we can finally blank it
          // out.
          //
          if (skip (pt))
          {
            pt = nullptr;
            continue;
          }
        }

        match_async (a, *pt, ctx.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));

        // Note: group already resolved in the previous loop.

        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.
            //
            const target& tp (p.search (t));
            const target& tp1 (p1.search (*pt));

            if (&tp != &tp1)
            {
              bool group (!p.prerequisite.belongs (t));

              const target_type& rtt (mod
                                      ? (group ? bmi::static_type : tts.bmi)
                                      : (group ? obj::static_type : tts.obj));

              fail << "synthesized dependency for prerequisite " << p << " "
                   << "would be incompatible with existing target " << *pt <<
                info << "existing prerequisite " << p1 << " does not match "
                   << p <<
                info << p1 << " resolves to target " << tp1 <<
                info << p << " resolves to target " << tp <<
                info << "specify corresponding " << rtt.name << "{} "
                   << "dependency explicitly";
            }

            break;
          }
        }
      }

      md.binless = binless;
      md.start = start;

      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
    {
      struct data
      {
        strings&             args;
        const file&          l;
        action               a;
        linfo                li;
        compile_target_types tts;
      } d {args, l, a, li, compile_types (li.type)};

      auto imp = [] (const file&, bool la)
      {
        return la;
      };

      auto lib = [&d, this] (const file* const* lc,
                             const string& p,
                             lflags f,
                             bool)
      {
        const file* l (lc != nullptr ? *lc : nullptr);

        if (l == nullptr)
        {
          // Don't try to link a library (whether -lfoo or foo.lib) to a
          // static library.
          //
          if (d.li.type != otype::a)
            d.args.push_back (p);
        }
        else
        {
          bool lu (l->is_a<libux> ());

          // The utility/non-utility case is tricky. Consider these two
          // scenarios:
          //
          // exe -> (libu1-e -> libu1-e) -> (liba) -> libu-a -> (liba1)
          // exe -> (liba) -> libu1-a -> libu1-a -> (liba1) -> libu-a1
          //
          // Libraries that should be linked are in '()'. That is, we need to
          // link the initial sequence of utility libraries and then, after
          // encountering a first non-utility, only link non-utilities
          // (because they already contain their utility's object files).
          //
          if (lu)
          {
            for (ptrdiff_t i (-1); lc[i] != nullptr; --i)
              if (!lc[i]->is_a<libux> ())
                return;
          }

          if (d.li.type == otype::a)
          {
            // Linking a utility library to a static library.
            //
            // Note that utility library prerequisites of utility libraries
            // are automatically handled by process_libraries(). So all we
            // have to do is implement the "thin archive" logic.
            //
            // We may also end up trying to link a non-utility library to a
            // static library via a utility library (direct linking is taken
            // care of by perform_update()). So we cut it off here.
            //
            if (!lu)
              return;

            if (l->mtime () == timestamp_unreal) // Binless.
              return;

            for (const target* pt: l->prerequisite_targets[d.a])
            {
              if (pt == nullptr)
                continue;

              if (modules)
              {
                if (pt->is_a<bmix> ()) // @@ MODHDR: hbmix{} has no objx{}
                  pt = find_adhoc_member (*pt, d.tts.obj);
              }

              // We could have dependency diamonds with utility libraries.
              // Repeats will be handled by the linker (in fact, it could be
              // required to repeat them to satisfy all the symbols) but here
              // we have to suppress duplicates ourselves.
              //
              if (const file* f = pt->is_a<objx> ())
              {
                string p (relative (f->path ()).string ());
                if (find (d.args.begin (), d.args.end (), p) == d.args.end ())
                  d.args.push_back (move (p));
              }
            }
          }
          else
          {
            // Linking a library to a shared library or executable.
            //

            if (l->mtime () == timestamp_unreal) // Binless.
              return;

            // On Windows a shared library is a DLL with the import library as
            // an ad hoc group member. MinGW though can link directly to DLLs
            // (see search_library() for details).
            //
            if (tclass == "windows" && l->is_a<libs> ())
            {
              if (const libi* li = find_adhoc_member<libi> (*l))
                l = li;
            }

            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
              {
                d.args.push_back ("-Wl,--whole-archive");
                d.args.push_back (move (p));
                d.args.push_back ("-Wl,--no-whole-archive");
                return;
              }
            }

            d.args.push_back (move (p));
          }
        }
      };

      auto opt = [&d, this] (const file& l,
                             const string& t,
                             bool com,
                             bool exp)
      {
        // Don't try to pass any loptions when linking a static library.
        //
        // Note also that we used to pass non-export loptions but that didn't
        // turn out to be very natural. Specifically, we would end up linking
        // things like version scripts (used to build the shared library
        // variant) when linking the static variant. So now any loptions must
        // be explicitly exported. Note that things are a bit fuzzy when it
        // comes to utility libraries so let's keep the original logic with
        // the exp checks below.
        //
        if (d.li.type == otype::a || !exp)
          return;

        // 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)
               : l.ctx.var_pool[t + (exp ? ".export.loptions" : ".loptions")]));

          append_options (d.args, *g, var);
        }
      };

      process_libraries (
        a, bs, li, sys_lib_dirs, l, la, lf, imp, lib, opt, true);
    }

    void link_rule::
    append_libraries (sha256& cs,
                      bool& update, timestamp mt,
                      const file& l, bool la, lflags lf,
                      const scope& bs, action a, linfo li) const
    {
      struct data
      {
        sha256&         cs;
        const dir_path& out_root;
        bool&           update;
        timestamp       mt;
        linfo           li;
      } d {cs, bs.root_scope ()->out_path (), update, mt, li};

      auto imp = [] (const file&, bool la)
      {
        return la;
      };

      auto lib = [&d, this] (const file* const* lc,
                             const string& p,
                             lflags f,
                             bool)
      {
        const file* l (lc != nullptr ? *lc : nullptr);

        if (l == nullptr)
        {
          if (d.li.type != otype::a)
            d.cs.append (p);
        }
        else
        {
          bool lu (l->is_a<libux> ());

          if (lu)
          {
            for (ptrdiff_t i (-1); lc[i] != nullptr; --i)
              if (!lc[i]->is_a<libux> ())
                return;
          }

          // We also don't need to do anything special for linking a utility
          // library to a static library. If any of its object files (or the
          // set of its object files) changes, then the library will have to
          // be updated as well. In other words, we use the library timestamp
          // as a proxy for all of its member's timestamps.
          //
          // We do need to cut of the static to static linking, just as in
          // append_libraries().
          //
          if (d.li.type == otype::a && !lu)
            return;

          if (l->mtime () == timestamp_unreal) // Binless.
            return;

          // 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
          // an ad hoc group member. MinGW though can link directly to DLLs
          // (see search_library() for details).
          //
          if (tclass == "windows" && l->is_a<libs> ())
          {
            if (const libi* li = find_adhoc_member<libi> (*l))
              l = li;
          }

          d.cs.append (f);
          hash_path (d.cs, l->path (), d.out_root);
        }
      };

      auto opt = [&d, this] (const file& l,
                             const string& t,
                             bool com,
                             bool exp)
      {
        if (d.li.type == otype::a || !exp)
          return;

        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)
               : l.ctx.var_pool[t + (exp ? ".export.loptions" : ".loptions")]));

          append_options (d.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 link) const
    {
      // Use -rpath-link only on targets that support it (Linux, *BSD). Note
      // that we don't really need it for top-level libraries.
      //
      if (link)
      {
        if (tclass != "linux" && tclass != "bsd")
          return;
      }

      auto imp = [link] (const file& l, bool la)
      {
        // If we are not rpath-link'ing, 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 (link ? !la : false) || l.is_a<libux> ();
      };

      // Package the data to keep within the 2-pointer small std::function
      // optimization limit.
      //
      struct
      {
        strings& args;
        bool     link;
      } d {args, link};

      auto lib = [&d, this] (const file* const* lc,
                             const string& f,
                             lflags,
                             bool sys)
      {
        const file* l (lc != nullptr ? *lc : nullptr);

        // 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;

          if (l->mtime () == timestamp_unreal) // Binless.
            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_type::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)
               : icasecmp (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.link ? "-Wl,-rpath-link," : "-Wl,-rpath,");

        size_t p (path::traits_type::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*, const string&, lflags, bool)> libf (lib);

      for (const prerequisite_target& pt: t.prerequisite_targets[a])
      {
        if (pt == nullptr)
          continue;

        bool la;
        const file* f;

        if ((la = (f = pt->is_a<liba>  ())) ||
            (la = (f = pt->is_a<libux> ())) ||
            (      f = pt->is_a<libs>  ()))
        {
          if (!link && !la)
          {
            // Top-level shared library dependency.
            //
            if (!f->path ().empty ()) // Not binless.
            {
              // 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 ());

      context& ctx (t.ctx);

      const scope& bs (t.base_scope ());
      const scope& rs (*bs.root_scope ());

      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);

      ltype lt (link_type (t));
      otype ot (lt.type);
      linfo li (link_info (bs, ot));
      compile_target_types tts (compile_types (ot));

      bool binless (md.binless);
      assert (ot != otype::e || !binless); // Sanity check.

      // Determine if we are out-of-date.
      //
      bool update (false);
      bool scratch (false);
      timestamp mt (binless ? timestamp_unreal : t.load_mtime ());

      // Update prerequisites. We determine if any relevant non-ad hoc ones
      // render us out-of-date manually below.
      //
      // Note that execute_prerequisites() blanks out all the ad hoc
      // prerequisites so we don't need to worry about them from now on.
      //
      target_state ts;

      if (optional<target_state> s =
          execute_prerequisites (a,
                                 t,
                                 mt,
                                 [] (const target&, size_t) {return false;}))
        ts = *s;
      else
      {
        // An ad hoc prerequisite renders us out-of-date. Let's update from
        // scratch for good measure.
        //
        scratch = update = true;
        ts = target_state::changed;
      }

      // Check for the for_install variable on each prerequisite and blank out
      // those that don't match. Note that we have to do it after updating
      // prerequisites to keep the dependency counts straight.
      //
      if (const variable* var_fi = ctx.var_pool.find ("for_install"))
      {
        // Parallel prerequisites/prerequisite_targets loop.
        //
        size_t i (md.start);
        for (prerequisite_member p: group_prerequisite_members (a, t))
        {
          const target*& pt (t.prerequisite_targets[a][i++]);

          if (pt == nullptr)
            continue;

          if (lookup l = p.prerequisite.vars[var_fi])
          {
            if (cast<bool> (l) != for_install)
            {
              l5 ([&]{trace << "excluding " << *pt << " due to for_install";});
              pt = nullptr;
            }
          }
        }
      }

      // (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 && lt.library () && !lt.utility)
      {
        bool la (lt.static_library ());

        pkgconfig_save (a, t, la, false /* common */, binless);

        // Generate the common .pc file if the lib{} rule is matched (see
        // apply() for details on this two-stage logic).
        //
        auto* m (find_adhoc_member<pc> (t)); // Will be pca/pcs if not found.

        if (!m->is_a (la ? pca::static_type : pcs::static_type))
        {
          if (t.group->matched (a))
            pkgconfig_save (a, t, la, true /* common */, binless);
          else
            // Mark as non-existent not to confuse the install rule.
            //
            m->mtime (timestamp_nonexistent);
        }
      }

      // If we have no binary to build then we are done.
      //
      if (binless)
      {
        t.mtime (timestamp_unreal);
        return ts;
      }

      // Open the dependency database (do it before messing with Windows
      // manifests to diagnose missing output directory).
      //
      depdb dd (tp + ".d");

      // Adjust the environment.
      //
      using environment = small_vector<string, 1>;
      environment env;
      sha256 env_cs;

      // If we have the search paths in the binutils pattern, prepend them to
      // the PATH environment variable so that any dependent tools (such as
      // mt.exe that is invoked by link.exe) are first search for in there.
      //
      {
        bin::pattern_paths pat (bin::lookup_pattern (rs));

        if (pat.paths != nullptr)
        {
          string v ("PATH=");
          v += pat.paths;

          env_cs.append (v); // Hash only what we are adding.

          if (optional<string> o = getenv ("PATH"))
          {
            v += path::traits_type::path_separator;
            v += *o;
          }

          env.push_back (move (v));
        }
      }

      // Convert the environment to what's expected by the process API.
      //
      small_vector<const char*, environment::small_size + 1> env_ptrs;
      if (!env.empty ())
      {
        for (const string& v: env)
          env_ptrs.push_back (v.c_str ());

        env_ptrs.push_back (nullptr);
      }

      // 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 && cast_true<bool> (t["bin.rpath.auto"]))
          rpath_timestamp = windows_rpath_timestamp (t, bs, a, li);

        auto p (windows_manifest (t, rpath_timestamp != timestamp_nonexistent));
        path& mf (p.first);
        timestamp mf_mt (p.second);

        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 == timestamp_nonexistent || mf_mt > mtime (manifest))
          {
            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);

            if (!ctx.dry_run)
            {
              auto_rmfile rm (of);

              try
              {
                process pr (rc,
                            args,
                            -1      /* stdin  */,
                            1       /* stdout */,
                            2       /* stderr */,
                            nullptr /* cwd    */,
                            env_ptrs.empty () ? nullptr : env_ptrs.data ());

                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 ();
                  rm.cancel ();
                }
                catch (const io_error& e)
                {
                  if (run_wait (args, pr))
                    fail << "unable to pipe resource file to " << args[0]
                         << ": " << e;

                  // If the child process has failed then assume the io error
                  // was caused by that and let run_finish() deal with it.
                }

                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 == timestamp_nonexistent || mf_mt > mt)
            update = true; // Manifest changed, force update.
        }
      }

      // Check/update the dependency database.
      //
      // 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"
               ? ctx.var_pool["bin.ld.checksum"]
               : x_checksum]));

        if (dd.expect (cs) != nullptr)
          l4 ([&]{trace << "linker mismatch forcing update of " << t;});
      }

      // Hash and compare any changes to the environment.
      //
      if (dd.expect (env_cs.string ()) != nullptr)
        l4 ([&]{trace << "environment 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.
      strings sargs;           // Argument tail with storage.

      // Stored args.
      //
      string arg1, arg2;
      strings sargs1;

      // Shallow-copy over stored args to args. Note that this must only be
      // done once we are finished appending to stored args because of
      // potential reallocations.
      //
      auto append_args = [&args] (const strings& sargs)
      {
        for (const string& a: sargs)
          args.push_back (a.c_str ());
      };

      if (lt.static_library ())
      {
        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])));

          // For utility libraries use thin archives if possible.
          //
          // LLVM's lib replacement had the /LLVMLIBTHIN option at least from
          // version 3.8 so we will assume always.
          //
          if (lt.utility)
          {
            const string& id (cast<string> (rs["bin.ar.id"]));

            if (id == "msvc-llvm")
              args.push_back ("/LLVMLIBTHIN");
          }
        }
        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 '-'.
          //
          arg1 = ranlib ? "rc" : "rcs";

          // For utility libraries use thin archives if possible.
          //
          // Thin archives are supported by GNU ar since binutils 2.19.1 and
          // LLVM ar since LLVM 3.8.0. Note that strictly speaking thin
          // archives also have to be supported by the linker but it is
          // probably safe to assume that the two came from the same version
          // of binutils/LLVM.
          //
          if (lt.utility)
          {
            const string& id (cast<string> (rs["bin.ar.id"]));

            for (bool g (id == "gnu"); g || id == "llvm"; ) // Breakout loop.
            {
              auto mj (cast<uint64_t> (rs["bin.ar.version.major"]));
              if (mj <  (g ? 2 : 3)) break;
              if (mj == (g ? 2 : 3))
              {
                auto mi (cast<uint64_t> (rs["bin.ar.version.minor"]));
                if (mi  < (g ? 18 : 8)) break;
                if (mi == 18 && g)
                {
                  auto pa (cast<uint64_t> (rs["bin.ar.version.patch"]));
                  if (pa < 1) break;
                }
              }

              arg1 += 'T';
              break;
            }
          }

          args.push_back (arg1.c_str ());
        }

        append_options (args, t, c_aoptions);
        append_options (args, t, x_aoptions);
      }
      else
      {
        // Are we using the compiler or the linker (e.g., link.exe) directly?
        //
        bool ldc (tsys != "win32-msvc");

        if (ldc)
        {
          append_options (args, t, c_coptions);
          append_options (args, t, x_coptions);
          append_options (args, tstd);
        }

        // Note that these come in the reverse order of coptions since the
        // library search paths are examined in the order specified (in
        // contrast to the "last value wins" semantics that we assume for
        // coptions).
        //
        append_options (args, t, x_loptions);
        append_options (args, t, c_loptions);

        // Handle soname/rpath.
        //
        if (tclass == "windows")
        {
          // Limited emulation for Windows with no support for user-defined
          // rpath/rpath-link.
          //
          lookup l;

          if ((l = t["bin.rpath"]) && !l->empty ())
            fail << ctgt << " does not support rpath";

          if ((l = t["bin.rpath_link"]) && !l->empty ())
            fail << ctgt << " does not support rpath-link";
        }
        else
        {
          // Set soname.
          //
          if (lt.shared_library ())
          {
            const libs_paths& paths (md.libs_paths);
            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.
              //
              arg1 = "-install_name";
              arg2 = "@rpath/" + leaf;
            }
            else
              arg1 = "-Wl,-soname," + leaf;

            if (!arg1.empty ())
              args.push_back (arg1.c_str ());

            if (!arg2.empty ())
              args.push_back (arg2.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.
          //
          if (cast_true<bool> (t[for_install
                                 ? "bin.rpath_link.auto"
                                 : "bin.rpath.auto"]))
            rpath_libraries (sargs, t, bs, a, li, for_install /* link */);

          lookup l;

          if ((l = t["bin.rpath"]) && !l->empty ())
            for (const dir_path& p: cast<dir_paths> (l))
              sargs.push_back ("-Wl,-rpath," + p.string ());

          if ((l = t["bin.rpath_link"]) && !l->empty ())
          {
            // Only certain targets support -rpath-link (Linux, *BSD).
            //
            if (tclass != "linux" && tclass != "bsd")
              fail << ctgt << " does not support rpath-link";

            for (const dir_path& p: cast<dir_paths> (l))
              sargs.push_back ("-Wl,-rpath-link," + p.string ());
          }
        }

        if (ldc)
          append_options (args, cmode);

        // Extra system library dirs (last).
        //
        assert (sys_lib_dirs_extra <= sys_lib_dirs.size ());

        if (tsys == "win32-msvc")
        {
          // If we have no LIB environment variable set, then we add all of
          // them. But we want extras to come first.
          //
          // Note that the mode options are added as part of cmode.
          //
          auto b (sys_lib_dirs.begin () + sys_lib_dirs_mode);
          auto m (sys_lib_dirs.begin () + sys_lib_dirs_extra);
          auto e (sys_lib_dirs.end ());

          for (auto i (m); i != e; ++i)
            sargs1.push_back ("/LIBPATH:" + i->string ());

          if (!getenv ("LIB"))
          {
            for (auto i (b); i != m; ++i)
              sargs1.push_back ("/LIBPATH:" + i->string ());
          }

          append_args (sargs1);
        }
        else
        {
          append_option_values (
            args,
            "-L",
            sys_lib_dirs.begin () + sys_lib_dirs_extra, sys_lib_dirs.end (),
            [] (const dir_path& d) {return d.string ().c_str ();});
        }
      }

      // 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]);

        // @@ Note that we don't hash output options so if one of the ad hoc
        //    members that we manage gets renamed, we will miss a rebuild.

        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 file names 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.
      //
      const file* def (nullptr); // Cached if present.
      {
        sha256 cs;

        for (const prerequisite_target& p: t.prerequisite_targets[a])
        {
          const target* pt (p.target);

          if (pt == nullptr)
            continue;

          // If this is bmi*{}, then obj*{} is its ad hoc member.
          //
          if (modules)
          {
            if (pt->is_a<bmix> ()) // @@ MODHDR: hbmix{} has no objx{}
              pt = find_adhoc_member (*pt, tts.obj);
          }

          const file* f;
          bool la (false), ls (false);

          // We link utility libraries to everything except other utility
          // libraries. In case of linking to liba{} we follow the "thin
          // archive" lead and "see through" to their object file
          // prerequisites (recursively, until we encounter a non-utility).
          //
          if ((f = pt->is_a<objx> ())           ||
              (!lt.utility &&
               (la = (f = pt->is_a<libux> ()))) ||
              (!lt.static_library () &&
               ((la = (f = pt->is_a<liba>  ())) ||
                (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)
            {
              append_libraries (cs, update, mt, *f, la, p.data, bs, a, li);
              f = nullptr; // Timestamp checked by hash_libraries().
            }
            else
              hash_path (cs, f->path (), rs.out_path ());
          }
          else if ((f = pt->is_a<bin::def> ()))
          {
            if (tclass == "windows" && !lt.static_library ())
            {
              // At least link.exe only allows a single .def file.
              //
              if (def != nullptr)
                fail << "multiple module definition files specified for " << t;

              hash_path (cs, f->path (), rs.out_path ());
              def = f;
            }
            else
              f = nullptr; // Not an input.
          }
          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 ())
          hash_path (cs, manifest, rs.out_path ());

        // Treat *.libs variable values as inputs, not options.
        //
        if (!lt.static_library ())
        {
          append_options (cs, t, c_libs);
          append_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.
      //
      if (dd.writing () || dd.mtime > mt)
        scratch = update = true;

      dd.close ();

      // If nothing changed, then we are done.
      //
      if (!update)
        return ts;

      // Ok, so we are updating. Finish building the command line.
      //
      string in, 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")
        {
          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 (ctype == compiler_type::clang)
          {
            // See the runtime selection code in the compile rule for details
            // on what's going on here.
            //
            initializer_list<const char*> os {"-nostdlib", "-nostartfiles"};
            if (!find_options (os, cmode)         &&
                !find_options (os, t, c_coptions) &&
                !find_options (os, t, x_coptions))
            {
              args.push_back ("/DEFAULTLIB:msvcrt");
              args.push_back ("/DEFAULTLIB:oldnames");
            }
          }

          // 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");
          args.push_back ("/DEFAULTLIB:user32");

          // 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 (def != nullptr)
          {
            in = "/DEF:" + relative (def->path ()).string ();
            args.push_back (in.c_str ());
          }

          // VC link.exe creates an import library and .exp file for an
          // executable if any of its object files export any symbols (think a
          // unit test linking libus{}). And, no, there is no way to suppress
          // it (but we can change their names with /IMPLIB). Well, there is a
          // way: create a .def file with an empty EXPORTS section, pass it to
          // lib.exe to create a dummy .exp (and .lib), and then pass this
          // empty .exp to link.exe. Wanna go this way? Didn't think so.
          //
          // Having no way to disable this, the next simplest thing seems to
          // be just cleaning this mess up. Note, however, that we better
          // change the default name since otherwise it will be impossible to
          // have a library and an executable with the same name in the same
          // directory (their .lib's will clash).
          //
          // Note also that if at some point we decide to support such "shared
          // executables" (-rdynamic, etc), then it will probably have to be a
          // different target type (exes{}?) since it will need a different set
          // of object files (-fPIC so probably objs{}), etc.
          //
          // Also, while we are at it, this means there could be a DLL without
          // an import library (which we currently don't handle very well).
          //
          out2 = "/IMPLIB:";

          if (ot == otype::s)
          {
            // On Windows libs{} is the DLL and an 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.
            //
            out2 += relative (find_adhoc_member<libi> (t)->path ()).string ();
          }
          else
          {
            out2 += relt.string ();
            out2 += ".lib";
          }

          args.push_back (out2.c_str ());

          // If we have /DEBUG then name the .pdb file. It is an ad hoc group
          // member.
          //
          if (find_option ("/DEBUG", args, true))
          {
            const file& pdb (
              *find_adhoc_member<file> (t, *bs.find_target_type ("pdb")));

            out1 = "/PDB:";
            out1 += relative (pdb.path ()).string ();
            args.push_back (out1.c_str ());
          }

          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 an ad hoc group member
                  // is the import library.
                  //
                  const file& imp (*find_adhoc_member<libi> (t));
                  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 ());

              // For MinGW the .def file is just another input.
              //
              if (def != nullptr)
              {
                in = relative (def->path ()).string ();
                args.push_back (in.c_str ());
              }

              break;
            }
          case compiler_class::msvc: assert (false);
          }
        }
      }

      args[0] = ld->recall_string ();

      // Append input files noticing the position of the first.
      //
#ifdef _WIN32
      size_t args_input (args.size ());
#endif

      // The same logic as during hashing above. See also a similar loop
      // inside append_libraries().
      //
      bool seen_obj (false);
      for (const prerequisite_target& p: t.prerequisite_targets[a])
      {
        const target* pt (p.target);

        if (pt == nullptr)
          continue;

        if (modules)
        {
          if (pt->is_a<bmix> ()) // @@ MODHDR: hbmix{} has no objx{}
            pt = find_adhoc_member (*pt, tts.obj);
        }

        const file* f;
        bool la (false), ls (false);

        if ((f = pt->is_a<objx> ())           ||
            (!lt.utility &&
             (la = (f = pt->is_a<libux> ()))) ||
            (!lt.static_library () &&
             ((la = (f = pt->is_a<liba>  ())) ||
              (ls = (f = pt->is_a<libs>  ())))))
        {
          if (la || ls)
            append_libraries (sargs, *f, la, p.data, bs, a, li);
          else
          {
            sargs.push_back (relative (f->path ()).string ()); // string()&&
            seen_obj = true;
          }
        }
      }

      // For MinGW manifest is an object file.
      //
      if (!manifest.empty () && tsys == "mingw32")
        sargs.push_back (relative (manifest).string ());

      // LLD misses an input file if we are linking only whole archives (LLVM
      // bug #43744, fixed in 9.0.1, 10.0.0). Repeating one of the previously-
      // mentioned archives seems to work around the issue.
      //
      if (!seen_obj             &&
          !lt.static_library () &&
          tsys == "win32-msvc"  &&
          cast<string> (rs["bin.ld.id"]) == "msvc-lld")
      {
        uint64_t mj;
        if ((mj = cast<uint64_t> (rs["bin.ld.version.major"])) < 9 ||
            (mj == 9 &&
             cast<uint64_t> (rs["bin.ld.version.minor"]) == 0 &&
             cast<uint64_t> (rs["bin.ld.version.patch"]) == 0))
        {
          auto i (find_if (sargs.rbegin (), sargs.rend (),
                           [] (const string& a)
                           {
                             return a.compare (0, 14, "/WHOLEARCHIVE:") == 0;
                           }));

          if (i != sargs.rend ())
            sargs.push_back (i->c_str () + 14);
        }
      }

      // Shallow-copy sargs over to args.
      //
      append_args (sargs);

      if (!lt.static_library ())
      {
        append_options (args, t, c_libs);
        append_options (args, t, x_libs);
      }

      args.push_back (nullptr);

      // Cleanup old (versioned) libraries. Let's do it even for dry-run to
      // keep things simple.
      //
      if (lt.shared_library ())
      {
        const libs_paths& paths (md.libs_paths);

        auto rm = [&paths, this] (path&& m, const string&, bool interm)
        {
          if (!interm)
          {
            // Filter out paths that match one of the current paths or a
            // prefix of the real path (the latter takes care of auxiliary
            // things like .d, .t, etc., that are normally derived from the
            // target name).
            //
            // Yes, we are basically ad hoc-excluding things that break. Maybe
            // we should use something more powerful for the pattern, such as
            // regex? We could have a filesystem pattern which we then filter
            // against a regex pattern?
            //
            auto prefix = [&m] (const path& p)
            {
              return path::traits_type::compare (m.string (),
                                                 p.string (),
                                                 p.string ().size ()) == 0;
            };

            if (!prefix (*paths.real) &&
                m !=  paths.interm    &&
                m !=  paths.soname    &&
                m !=  paths.load      &&
                m !=  paths.link)
            {
              try_rmfile (m);

              if (m.extension () != "d")
              {
                try_rmfile (m + ".d");

                if (tsys == "win32-msvc")
                {
                  try_rmfile (m.base () += ".ilk");
                  try_rmfile (m += ".pdb");
                }
              }
            }
          }
          return true;
        };

        auto clean = [&rm] (const path& p)
        {
          try
          {
            if (verb >= 4) // Seeing this with -V doesn't really add any value.
              text << "rm " << p;

            // Note: doesn't follow symlinks.
            //
            path_search (p,
                         rm,
                         dir_path () /* start */,
                         path_match_flags::none);
          }
          catch (const system_error&) {} // Ignore errors.
        };

        if (!paths.clean_load.empty ())    clean (paths.clean_load);
        if (!paths.clean_version.empty ()) clean (paths.clean_version);
      }
      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 archiever
        // to complain.
        //
        if (mt != timestamp_nonexistent)
          try_rmfile (relt, true);
      }

      if (verb == 1)
        text << (lt.static_library () ? "ar " : "ld ") << t;
      else if (verb == 2)
        print_process (args);

      // Do any necessary fixups to the command line to make it runnable.
      //
      // Notice the split in the diagnostics: at verbosity level 1 we print
      // the "logical" command line while at level 2 and above -- what we are
      // actually executing.
      //
      // On Windows we need to deal with the command line length limit. The
      // best workaround seems to be passing (part of) the command line in an
      // "options file" ("response file" in Microsoft's terminology). Both
      // Microsoft's link.exe/lib.exe as well as GNU g??.exe/ar.exe support
      // the same @<file> notation (and with a compatible subset of the
      // content format; see below). Note also that GCC is smart enough to use
      // an options file to call the underlying linker if we called it with
      // @<file>. We will also assume that any other linker that we might be
      // using supports this notation.
      //
      // Note that this is a limitation of the host platform, not the target
      // (and Wine, where these lines are a bit blurred, does not have this
      // length limitation).
      //
#ifdef _WIN32
      auto_rmfile trm;
      string targ;
      {
        // Calculate the would-be command line length similar to how process'
        // implementation does it.
        //
        auto quote = [s = string ()] (const char* a) mutable -> const char*
        {
          return process::quote_argument (a, s);
        };

        size_t n (0);
        for (const char* a: args)
        {
          if (a != nullptr)
          {
            if (n != 0)
              n++; // For the space separator.

            n += strlen (quote (a));
          }
        }

        if (n > 32766) // 32768 - "Unicode terminating null character".
        {
          // Use the .t extension (for "temporary").
          //
          const path& f ((trm = auto_rmfile (relt + ".t")).path);

          try
          {
            ofdstream ofs (f);

            // Both Microsoft and GNU support a space-separated list of
            // potentially-quoted arguments. GNU also supports backslash-
            // escaping (whether Microsoft supports it is unclear; but it
            // definitely doesn't need it for backslashes themselves, for
            // example, in paths).
            //
            bool e (tsys != "win32-msvc"); // Assume GNU if not MSVC.
            string b;

            for (size_t i (args_input), n (args.size () - 1); i != n; ++i)
            {
              const char* a (args[i]);

              if (e) // We will most likely have backslashes so just do it.
              {
                for (b.clear (); *a != '\0'; ++a)
                {
                  if (*a != '\\')
                    b += *a;
                  else
                    b += "\\\\";
                }

                a = b.c_str ();
              }

              ofs << (i != args_input ? " " : "") << quote (a);
            }

            ofs << '\n';
            ofs.close ();
          }
          catch (const io_error& e)
          {
            fail << "unable to write to " << f << ": " << e;
          }

          // Replace input arguments with @file.
          //
          targ = '@' + f.string ();
          args.resize (args_input);
          args.push_back (targ.c_str());
          args.push_back (nullptr);

          //@@ TODO: leave .t file if linker failed and verb > 2?
        }
      }
#endif

      if (verb > 2)
        print_process (args);

      // Remove the target file if any of the subsequent (after the linker)
      // actions fail or if the linker fails but does not clean up its mess
      // (like link.exe). If we don't do that, then we will end up with a
      // broken build that is up-to-date.
      //
      auto_rmfile rm;

      if (!ctx.dry_run)
      {
        rm = auto_rmfile (relt);

        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 linker that may try to pull off
          // something like this) we are going to redirect stdout to stderr.
          // For sane compilers this should be harmless.
          //
          // Note that we don't need this for LLD's link.exe replacement which
          // is quiet.
          //
          bool filter (tsys == "win32-msvc"  &&
                       !lt.static_library () &&
                       cast<string> (rs["bin.ld.id"]) != "msvc-lld");

          process pr (*ld,
                      args.data (),
                      0                  /* stdin  */,
                      (filter ? -1 : 2)  /* stdout */,
                      2                  /* stderr */,
                      nullptr            /* cwd    */,
                      env_ptrs.empty () ? nullptr : env_ptrs.data ());

          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)
          {
            rm.cancel ();
#ifdef _WIN32
            trm.cancel ();
#endif
            exit (1);
          }

          throw failed ();
        }

        // Clean up executable's import library (see above for details).
        //
        if (lt.executable () && tsys == "win32-msvc")
        {
          try_rmfile (relt + ".lib", true /* ignore_errors */);
          try_rmfile (relt + ".exp", true /* ignore_errors */);
        }
      }

      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);

        if (!ctx.dry_run)
          run (rl,
               args,
               dir_path () /* cwd */,
               env_ptrs.empty () ? nullptr : env_ptrs.data ());
      }

      // For Windows generate (or clean up) rpath-emulating assembly.
      //
      if (tclass == "windows")
      {
        if (lt.executable ())
          windows_rpath_assembly (t, bs, a, li,
                                  cast<string> (rs[x_target_cpu]),
                                  rpath_timestamp,
                                  scratch);
      }

      if (lt.shared_library ())
      {
        // For shared libraries we may need to create a bunch of symlinks (or
        // fallback to hardlinks/copies on Windows).
        //
        auto ln = [&ctx] (const path& f, const path& l)
        {
          if (verb >= 3)
            text << "ln -sf " << f << ' ' << l;

          if (ctx.dry_run)
            return;

          try
          {
            try
            {
              // The -f part.
              //
              if (file_exists (l, false /* follow_symlinks */))
                try_rmfile (l);

              mkanylink (f, l, true /* copy */, true /* relative */);
            }
            catch (system_error& e)
            {
              throw pair<entry_type, system_error> (entry_type::symlink,
                                                    move (e));
            }
          }
          catch (const pair<entry_type, system_error>& e)
          {
            const char* w (e.first == entry_type::regular ? "copy"     :
                           e.first == entry_type::symlink ? "symlink"  :
                           e.first == entry_type::other   ? "hardlink" :
                           nullptr);

            fail << "unable to make " << w << ' ' << l << ": " << e.second;
          }
        };

        const libs_paths& paths (md.libs_paths);

        const path& lk (paths.link);
        const path& ld (paths.load);
        const path& so (paths.soname);
        const path& in (paths.interm);

        const path* f (paths.real);

        if (!in.empty ()) {ln (*f, in); f = &in;}
        if (!so.empty ()) {ln (*f, so); f = &so;}
        if (!ld.empty ()) {ln (*f, ld); f = &ld;}
        if (!lk.empty ()) {ln (*f, lk);}
      }
      else if (lt.static_library ())
      {
        // Apple ar (from cctools) for some reason truncates fractional
        // seconds when running on APFS (HFS has a second resolution so it's
        // not an issue there). This can lead to object files being newer than
        // the archive, which is naturally bad news. Filed as bug 49604334,
        // reportedly fixed in Xcode 11 beta 5.
        //
        // Note that this block is not inside #ifdef __APPLE__ because we
        // could be cross-compiling, theoretically. We also make sure we use
        // Apple's ar (which is (un)recognized as 'generic') instead of, say,
        // llvm-ar.
        //
        if (tsys == "darwin" && cast<string> (rs["bin.ar.id"]) == "generic")
        {
          if (!ctx.dry_run)
            touch (ctx, tp, false /* create */, verb_never);
        }
      }

      if (!ctx.dry_run)
      {
        rm.cancel ();
        dd.check_mtime (tp);
      }

      // 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. Plus, in
      // case of dry-run, the file won't be modified.
      //
      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));
      const match_data& md (t.data<match_data> ());

      clean_extras extras;
      clean_adhoc_extras adhoc_extras;

      if (md.binless)
        ; // Clean prerequsites/members.
      else
      {
        if (tclass != "windows")
          ; // Everything is the default.
        else if (tsys == "mingw32")
        {
          if (lt.executable ())
          {
            extras = {".d", ".dlls/", ".manifest.o", ".manifest"};
          }

          // For shared and static library it's the default.
        }
        else
        {
          // Assuming MSVC or alike.
          //
          if (lt.executable ())
          {
            // Clean up .ilk in case the user enabled incremental linking
            // (notice that the .ilk extension replaces .exe).
            //
            extras = {".d", ".dlls/", ".manifest", "-.ilk"};
          }
          else if (lt.shared_library ())
          {
            // Clean up .ilk and .exp.
            //
            // Note that .exp is based on the .lib, not .dll name. And with
            // versioning their bases may not be the same.
            //
            extras = {".d", "-.ilk"};
            adhoc_extras.push_back ({libi::static_type, {"-.exp"}});
          }

          // For static library it's the default.
        }

        if (extras.empty ())
          extras = {".d"}; // Default.

#ifdef _WIN32
        extras.push_back (".t"); // Options file.
#endif
        // For shared libraries we may have a bunch of symlinks that we need
        // to remove.
        //
        if (lt.shared_library ())
        {
          const libs_paths& lp (md.libs_paths);

          auto add = [&extras] (const path& p)
          {
            if (!p.empty ())
              extras.push_back (p.string ().c_str ());
          };

          add (lp.link);
          add (lp.load);
          add (lp.soname);
          add (lp.interm);
        }
      }

      return perform_clean_extra (a, t, extras, adhoc_extras);
    }
  }
}