From 4bdf53837e010073de802070d4e6087410662d3e Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 24 Aug 2019 17:41:30 +0300 Subject: Move cc build system module to separate library --- libbuild2/cc/link-rule.cxx | 3043 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3043 insertions(+) create mode 100644 libbuild2/cc/link-rule.cxx (limited to 'libbuild2/cc/link-rule.cxx') diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx new file mode 100644 index 0000000..110a992 --- /dev/null +++ b/libbuild2/cc/link-rule.cxx @@ -0,0 +1,3043 @@ +// file : libbuild2/cc/link-rule.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include // exit() +#include // strlen() + +#include // file_exists() + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include // c, pc* +#include + +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 1") + { + 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 () || + // Header-only C library. + (library && p.is_a ())) + { + r.seen_c = r.seen_c || true; + } + else if (p.is_a () || p.is_a ()) + { + r.seen_obj = r.seen_obj || true; + } + else if (p.is_a () || p.is_a ()) + { + // 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 () || p.is_a ()) + { + if (ot != otype::a) + fail << p.type ().name << "{} as prerequisite of " << t; + + r.seen_obj = r.seen_obj || true; + } + else if (p.is_a () || p.is_a ()) + { + if (ot != otype::s) + fail << p.type ().name << "{} as prerequisite of " << t; + + r.seen_obj = r.seen_obj || true; + } + else if (p.is_a () || p.is_a ()) + { + // 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 linkup 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 ()) + { + 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 (), + 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 ()) + { + // 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 () ? 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 () || + p.is_a () || + p.is_a ()) + { + 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 () && !(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 (t["bin.lib.load_suffix"])); + + // Figure out the version. + // + string ver; + using verion_map = map; + if (const verion_map* m = cast_null (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 ("*"); + + // At this stage the only platform-specific version we support is the + // "no version" override. + // + if (i != m->end () && !i->second.empty ()) + fail << i->first << "-specific bin.lib.version not yet supported"; + + // Finally look for the platform-independent version. + // + if (i == m->end ()) + i = m->find (""); + + // If we didn't find anything, fail. If the bin.lib.version was + // specified, then it should explicitly handle all the targets. + // + if (i == m->end ()) + fail << "no version for " << ctgt << " in bin.lib.version" << + info << "considere adding " << tsys << "@ or " << tclass + << "@"; + + 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 pattern. + // + path cp (b); + cp += "?*"; // Don't match empty (like the libfoo.so symlink). + append_ext (cp); + + // 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 (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 ()) + { + b += ls; + + // We will only need the load name if the following name differs. + // + if (!ver.empty ()) + { + ld = b; + append_ext (ld); + } + } + + if (!ver.empty ()) + b += ver; + + const path& re (t.derive_path (move (b))); + + return libs_paths { + move (lk), move (ld), move (so), move (in), &re, move (cp)}; + } + + // 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 ()) + { + pf = &link_member (*ul, a, li)->as (); + } + else if ((pf = pt->is_a ()) || + (pf = pt->is_a ()) || + (pf = pt->is_a ())) + ; + 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 ()); + 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 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 ()) + { + 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 () || + p.is_a () || + p.is_a () || + p.is_a ()) + { + // 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 ()) + 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 ()) pt = &search (t, tts.obj, p.key ()); + else if (p.is_a ()) 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 () && 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 () && !p.is_a ()) + { + // @@ 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 () || pt->is_a ()); + + 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 (l).c_str (); + if (auto l = t[ot == otype::e ? "bin.exe.suffix" : "bin.lib.suffix"]) + s = cast (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 (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 (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. + // + if (ot != otype::e) + { + file& pc (add_adhoc_member (t, + (ot == otype::a + ? pca::static_type + : pcs::static_type))); + + // Note that here we always use the lib name prefix, even on + // Windows with VC. The reason is the user needs a consistent name + // across platforms by which they can 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. + // + if (pc.path ().empty ()) + pc.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 (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). + // + // 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 () || + p.is_a () || p.is_a () || p.is_a () || + p.is_a () || 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 ()) + { + src = true; + continue; // Check the rest of the prerequisites. + } + + // Ignore some known target types (fsdir, headers, libraries, + // modules). + // + if (p1.is_a () || + p1.is_a () || + p1.is_a () || p1.is_a () || p1.is_a () || + p1.is_a () || p1.is_a () || + (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) || + (p.is_a () && p1.is_a ())) + 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 ()) || pt->is_a ()) + { + 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->find_original (var, true).first; + + if (!l.defined ()) + { + bool g (pt->group != nullptr); + l = bs.find_original (var, + &pt->type (), + &pt->name, + (g ? &pt->group->type () : nullptr), + (g ? &pt->group->name : nullptr)).first; + } + + if (l ? cast (*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 ()) + { + // 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 ()); + + // 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 ()) + 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 ()) // @@ 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 ()) + { + 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 ()) + { + if (const libi* li = find_adhoc_member (*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. + // + if (d.li.type == otype::a) + return; + + // If we need an interface value, then use the group (lib{}). + // + if (const target* g = exp && l.is_a () ? 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:: + hash_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 ()); + + if (lu) + { + for (ptrdiff_t i (-1); lc[i] != nullptr; --i) + if (!lc[i]->is_a ()) + 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 ()) + { + if (const libi* li = find_adhoc_member (*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) + return; + + if (const target* g = exp && l.is_a () ? 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")])); + + hash_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 (); + }; + + // 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 ()) + 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) + : casecmp (f.c_str () + p, e)) != 0) + return; + } + + // Ok, if we are here then it means we have a non-system, shared + // library and its absolute path is in f. + // + string o (d.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 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 ())) || + (la = (f = pt->is_a ())) || + ( f = pt->is_a ())) + { + 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 (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 ()); + 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 ()); + + // 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 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 (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) + pkgconfig_save (a, t, lt.static_library (), binless); + + // 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"); + + // 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 (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 (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); + + 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 (pr.wait ()) // Ignore if child failed. + fail << "unable to pipe resource file to " << args[0] + << ": " << e; + } + + run_finish (args, pr); + } + catch (const process_error& e) + { + error << "unable to execute " << args[0] << ": " << e; + + if (e.child) + exit (1); + + throw failed (); + } + } + + update = true; // Manifest changed, force update. + } + } + else + { + manifest = move (mf); // Save for link.exe's /MANIFESTINPUT. + + if (mf_mt == 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 (rs["bin.ranlib.checksum"]).c_str () + : "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + + if (dd.expect (cast (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 ( + 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;}); + } + + // Next check the target. While it might be incorporated into the linker + // checksum, it also might not (e.g., VC link.exe). + // + if (dd.expect (ctgt.string ()) != nullptr) + l4 ([&]{trace << "target mismatch forcing update of " << t;}); + + // Start building the command line. While we don't yet know whether we + // will really need it, we need to hash it to find out. So the options + // are to either replicate the exact process twice, first for hashing + // then for building or to go ahead and start building and hash the + // result. The first approach is probably more efficient while the + // second is simpler. Let's got with the simpler for now (actually it's + // kind of a hybrid). + // + cstrings args {nullptr}; // Reserve one for config.bin.ar/config.x. + + // Storage. + // + string arg1, arg2; + strings sargs; + + 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 (rs[x_target_cpu]))); + } + 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 (rs["bin.ar.id"])); + + for (bool g (id == "gnu"); g || id == "llvm"; ) // Breakout loop. + { + auto mj (cast (rs["bin.ar.version.major"])); + if (mj < (g ? 2 : 3)) break; + if (mj == (g ? 2 : 3)) + { + auto mi (cast (rs["bin.ar.version.minor"])); + if (mi < (g ? 18 : 8)) break; + if (mi == 18 && g) + { + auto pa (cast (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 + { + if (tsys == "win32-msvc") + { + // We are using link.exe directly so don't pass the compiler + // options. + } + else + { + append_options (args, t, c_coptions); + append_options (args, t, x_coptions); + append_options (args, tstd); + } + + append_options (args, t, c_loptions); + append_options (args, t, x_loptions); + + // Extra system library dirs (last). + // + // @@ /LIBPATH:, not /LIBPATH + // + assert (sys_lib_dirs_extra <= sys_lib_dirs.size ()); + append_option_values ( + args, + cclass == compiler_class::msvc ? "/LIBPATH:" : "-L", + sys_lib_dirs.begin () + sys_lib_dirs_extra, sys_lib_dirs.end (), + [] (const dir_path& d) {return d.string ().c_str ();}); + + // Handle soname/rpath. + // + if (tclass == "windows") + { + // Limited emulation for Windows with no support for user-defined + // 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 (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 (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 (l)) + sargs.push_back ("-Wl,-rpath-link," + p.string ()); + } + } + } + + // All the options should now be in. Hash them and compare with the db. + // + { + sha256 cs; + + for (size_t i (1); i != args.size (); ++i) + cs.append (args[i]); + + for (size_t i (0); i != sargs.size (); ++i) + cs.append (sargs[i]); + + // @@ 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 ()) // @@ 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 ()) || + (!lt.utility && + (la = (f = pt->is_a ()))) || + (!lt.static_library () && + ((la = (f = pt->is_a ())) || + (ls = (f = pt->is_a ()))))) + { + // Link all the dependent interface libraries (shared) or interface + // and implementation (static), recursively. + // + // Also check if any of them render us out of date. The tricky + // case is, say, a utility library (static) that depends on a + // shared library. When the shared library is updated, there is no + // reason to re-archive the utility but those who link the utility + // have to "see through" the changes in the shared library. + // + if (la || ls) + { + hash_libraries (cs, update, mt, *f, la, p.data, bs, a, li); + f = nullptr; // Timestamp checked by hash_libraries(). + } + else + hash_path (cs, f->path (), rs.out_path ()); + } + else if ((f = pt->is_a ())) + { + 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 (); // 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 ()) + { + hash_options (cs, t, c_libs); + hash_options (cs, t, x_libs); + } + + if (dd.expect (cs.string ()) != nullptr) + l4 ([&]{trace << "file set mismatch forcing update of " << t;}); + } + + // If any of the above checks resulted in a mismatch (different linker, + // options or input file set), or if the database is newer than the + // target (interrupted update) then force the target update. Also note + // this situation in the "from scratch" flag. + // + 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 (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 (rs["bin.ld.path"]); + args.push_back ("/NOLOGO"); + + if (ot == otype::s) + args.push_back ("/DLL"); + + // Add /MACHINE. + // + args.push_back (msvc_machine (cast (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) + { + // According to Clang's MSVC.cpp, we shall link libcmt.lib (static + // multi-threaded runtime) unless -nostdlib or -nostartfiles is + // specified. + // + if (!find_options ({"-nostdlib", "-nostartfiles"}, t, c_coptions) && + !find_options ({"-nostdlib", "-nostartfiles"}, t, x_coptions)) + args.push_back ("/DEFAULTLIB:libcmt.lib"); + } + + // If you look at the list of libraries Visual Studio links by + // default, it includes everything and a couple of kitchen sinks + // (winspool32.lib, ole32.lib, odbc32.lib, etc) while we want to + // keep our low-level build as pure as possible. However, there seem + // to be fairly essential libraries that are not linked by link.exe + // by default (use /VERBOSE:LIB to see the list). For example, MinGW + // by default links advapi32, shell32, user32, and kernel32. And so + // we follow suit and make sure those are linked. advapi32 and + // kernel32 are already on the default list and we only need to add + // the other two. + // + // The way we are going to do it is via the /DEFAULTLIB option + // rather than specifying the libraries as normal inputs (as VS + // does). This way the user can override our actions with the + // /NODEFAULTLIB option. + // + args.push_back ("/DEFAULTLIB:shell32.lib"); + args.push_back ("/DEFAULTLIB:user32.lib"); + + // Take care of the manifest (will be empty for the DLL). + // + if (!manifest.empty ()) + { + out3 = "/MANIFESTINPUT:"; + out3 += relative (manifest).string (); + args.push_back ("/MANIFEST:EMBED"); + args.push_back (out3.c_str ()); + } + + if (def != nullptr) + { + in = "/DEF:" + relative (def->path ()).string (); + args.push_back (in.c_str ()); + } + + 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. + // + const file& imp (*find_adhoc_member (t)); + + out2 = "/IMPLIB:"; + out2 += relative (imp.path ()).string (); + 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 (t, *bs.find_target_type ("pdb"))); + + out1 = "/PDB:"; + out1 += relative (pdb.path ()).string (); + args.push_back (out1.c_str ()); + } + + // @@ An executable can have an import library and VS seems to + // always name it. I wonder what would trigger its generation? + // Could it be the presence of export symbols? Yes, link.exe will + // generate the import library iff there are exported symbols. + // Which means there could be a DLL without an import library + // (which we currently don't handle very well). + // + out = "/OUT:" + relt.string (); + args.push_back (out.c_str ()); + } + else + { + switch (cclass) + { + case compiler_class::gcc: + { + ld = &cpath; + + // Add the option that triggers building a shared library and + // take care of any extras (e.g., import library). + // + if (ot == otype::s) + { + if (tclass == "macos") + args.push_back ("-dynamiclib"); + else + args.push_back ("-shared"); + + if (tsys == "mingw32") + { + // On Windows libs{} is the DLL and an ad hoc group member + // is the import library. + // + const file& imp (*find_adhoc_member (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(). + // + for (const prerequisite_target& p: t.prerequisite_targets[a]) + { + const target* pt (p.target); + + if (pt == nullptr) + continue; + + if (modules) + { + if (pt->is_a ()) // @@ 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 ()) || + (!lt.utility && + (la = (f = pt->is_a ()))) || + (!lt.static_library () && + ((la = (f = pt->is_a ())) || + (ls = (f = pt->is_a ()))))) + { + if (la || ls) + append_libraries (sargs, *f, la, p.data, bs, a, li); + else + sargs.push_back (relative (f->path ()).string ()); // string()&& + } + } + + // For MinGW manifest is an object file. + // + if (!manifest.empty () && tsys == "mingw32") + sargs.push_back (relative (manifest).string ()); + + // Shallow-copy sargs to args. Why not do it as we go along pushing into + // sargs? Because of potential reallocations in sargs. + // + for (const string& a: sargs) + args.push_back (a.c_str ()); + + if (!lt.static_library ()) + { + append_options (args, t, c_libs); + append_options (args, t, x_libs); + } + + args.push_back (nullptr); + + // Cleanup old (versioned) libraries. Let's do it even for dry-run to + // keep things simple. + // + if (lt.shared_library ()) + { + const libs_paths& paths (md.libs_paths); + const path& p (paths.clean); + + if (!p.empty ()) + try + { + if (verb >= 4) // Seeing this with -V doesn't really add any value. + text << "rm " << p; + + auto rm = [&paths, this] (path&& m, const string&, bool interm) + { + if (!interm) + { + // Filter out paths that have one of the current paths as a + // prefix. + // + auto test = [&m] (const path& p) + { + const string& s (p.string ()); + return s.empty () || m.string ().compare (0, s.size (), s) != 0; + }; + + if (test (*paths.real) && + test ( paths.interm) && + test ( paths.soname) && + test ( paths.load) && + test ( paths.link)) + { + try_rmfile (m); + try_rmfile (m + ".d"); + + if (tsys == "win32-msvc") + { + try_rmfile (m.base () += ".ilk"); + try_rmfile (m += ".pdb"); + } + } + } + return true; + }; + + // Note: doesn't follow symlinks. + // + path_search (p, rm, dir_path () /* start */, path_match_flags::none); + } + catch (const system_error&) {} // Ignore errors. + } + else if (lt.static_library ()) + { + // We use relative paths to the object files which means we may end + // up with different ones depending on CWD and some implementation + // treat them as different archive members. So remote the file to + // be sure. Note that we ignore errors leaving it to the 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 @ 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 + // @. 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 " << 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. + // + bool filter (tsys == "win32-msvc" && !lt.static_library ()); + + process pr (*ld, args.data (), 0, (filter ? -1 : 2)); + + if (filter) + { + try + { + ifdstream is ( + move (pr.in_ofd), fdstream_mode::text, ifdstream::badbit); + + msvc_filter_link (is, t, ot); + + // If anything remains in the stream, send it all to stderr. + // Note that the eof check is important: if the stream is at + // eof, this and all subsequent writes to the diagnostics stream + // will fail (and you won't see a thing). + // + if (is.peek () != ifdstream::traits_type::eof ()) + diag_stream_lock () << is.rdbuf (); + + is.close (); + } + catch (const io_error&) {} // Assume exits with error. + } + + run_finish (args, pr); + } + catch (const process_error& e) + { + error << "unable to execute " << args[0] << ": " << e; + + // In a multi-threaded program that fork()'ed but did not exec(), it + // is unwise to try to do any kind of cleanup (like unwinding the + // stack and running destructors). + // + if (e.child) + { + rm.cancel (); +#ifdef _WIN32 + trm.cancel (); +#endif + exit (1); + } + + throw failed (); + } + + // 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. 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 the mess up. + // + // 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. + // + if (lt.executable () && tsys == "win32-msvc") + { + path b (relt.base ()); + try_rmfile (b + ".lib", true /* ignore_errors */); + try_rmfile (b + ".exp", true /* ignore_errors */); + } + } + + if (ranlib) + { + const process_path& rl (cast (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); + } + + // For Windows generate (or clean up) rpath-emulating assembly. + // + if (tclass == "windows") + { + if (lt.executable ()) + windows_rpath_assembly (t, bs, a, li, + cast (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::symlink, + move (e)); + } + } + catch (const pair& 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 = ∈} + 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 (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 ()); + + ltype lt (link_type (t)); + const match_data& md (t.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); + } + } +} -- cgit v1.1