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 --- build2/cc/link-rule.cxx | 3043 ----------------------------------------------- 1 file changed, 3043 deletions(-) delete mode 100644 build2/cc/link-rule.cxx (limited to 'build2/cc/link-rule.cxx') diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx deleted file mode 100644 index adf76d1..0000000 --- a/build2/cc/link-rule.cxx +++ /dev/null @@ -1,3043 +0,0 @@ -// file : build2/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