diff options
Diffstat (limited to 'build2/cc/link.cxx')
-rw-r--r-- | build2/cc/link.cxx | 405 |
1 files changed, 238 insertions, 167 deletions
diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index b113adb..7b14621 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -56,15 +56,18 @@ namespace build2 // (i.e., a utility library). // - otype lt (link_type (t)); + ltype lt (link_type (t)); + otype ot (lt.type); - // If this is a library, link-up to our group (this is the lib{} target - // group protocol which means this can be done whether we match or not). + // If this is a library, link-up to our group (this is the target group + // protocol which means this can be done whether we match or not). // - if (lt == otype::a || lt == otype::s) + if (lt.library ()) { if (t.group == nullptr) - t.group = targets.find<lib> (t.dir, t.out, t.name); + t.group = targets.find ( + lt.utility ? libu::static_type : lib::static_type, + t.dir, t.out, t.name); } // Scan prerequisites and see if we can work with what we've got. Note @@ -90,28 +93,29 @@ namespace build2 } else if (p.is_a<obje> () || p.is_a<bmie> ()) { - if (lt != otype::e) + if (ot != otype::e) fail << p.type ().name << "{} as prerequisite of " << t; seen_obj = seen_obj || true; } else if (p.is_a<obja> () || p.is_a<bmia> ()) { - if (lt != otype::a) + if (ot != otype::a) fail << p.type ().name << "{} as prerequisite of " << t; seen_obj = seen_obj || true; } else if (p.is_a<objs> () || p.is_a<bmis> ()) { - if (lt != otype::s) + if (ot != otype::s) fail << p.type ().name << "{} as prerequisite of " << t; seen_obj = seen_obj || true; } - else if (p.is_a<lib> () || - p.is_a<liba> () || - p.is_a<libs> ()) + else if (p.is_a<libx> () || + p.is_a<liba> () || + p.is_a<libs> () || + p.is_a<libux> ()) { seen_lib = seen_lib || true; } @@ -300,12 +304,13 @@ namespace build2 const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); - otype lt (link_type (t)); - lorder lo (link_order (bs, lt)); + ltype lt (link_type (t)); + otype ot (lt.type); + linfo li (link_info (bs, ot)); // Set the library type (C, C++, etc). // - if (lt != otype::e) + if (lt.library ()) t.vars.assign (c_type) = string (x); // Derive file name(s) and add ad hoc group members. @@ -325,57 +330,119 @@ namespace build2 const char* p (nullptr); // Prefix. const char* s (nullptr); // Suffix. - switch (lt) + if (lt.utility) { - case otype::e: + // 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") { - if (tclass == "windows") - e = "exe"; - else - e = ""; + 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 (auto l = t["bin.exe.prefix"]) p = cast<string> (l).c_str (); - if (auto l = t["bin.exe.suffix"]) s = cast<string> (l).c_str (); + 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; + } - t.derive_path (e, p, s); - 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; + } + } } - case otype::a: + + t.derive_path (e, p, s); + } + else + { + switch (ot) { - if (cid == compiler_id::msvc) - e = "lib"; - else + case otype::e: { - p = "lib"; - e = "a"; + if (tclass == "windows") + e = "exe"; + else + e = ""; + + if (auto l = t["bin.exe.prefix"]) p = cast<string> (l).c_str (); + if (auto l = t["bin.exe.suffix"]) s = cast<string> (l).c_str (); + + t.derive_path (e, p, s); + break; } + case otype::a: + { + if (cid == compiler_id::msvc) + e = "lib"; + else + { + p = "lib"; + e = "a"; + } - if (auto l = t["bin.lib.prefix"]) p = cast<string> (l).c_str (); - if (auto l = t["bin.lib.suffix"]) s = cast<string> (l).c_str (); + if (auto l = t["bin.lib.prefix"]) p = cast<string> (l).c_str (); + if (auto l = t["bin.lib.suffix"]) s = cast<string> (l).c_str (); - t.derive_path (e, p, s); - break; - } - case otype::s: - { - // On Windows libs{} is an ad hoc group. The libs{} itself is the - // DLL and we add libi{} import library as its member. - // - if (tclass == "windows") - libi = add_adhoc (t, "libi"); + t.derive_path (e, p, s); + break; + } + case otype::s: + { + // On Windows libs{} is an ad hoc group. The libs{} itself is + // the DLL and we add libi{} import library as its member. + // + if (tclass == "windows") + libi = add_adhoc (t, "libi"); - t.data (derive_libs_paths (t)); // Cache in target. + t.data (derive_libs_paths (t)); // Cache in target. - if (libi) - match_recipe (libi, group_recipe); // Set recipe and unlock. + if (libi) + match_recipe (libi, group_recipe); // Set recipe and unlock. - break; + break; + } } } // PDB // - if (lt != otype::a && + if (!lt.static_library () && cid == compiler_id::msvc && (find_option ("/DEBUG", t, c_loptions, true) || find_option ("/DEBUG", t, x_loptions, true))) @@ -419,7 +486,7 @@ namespace build2 // or a subdirectory of our project root. // optional<dir_paths> usr_lib_dirs; // Extract lazily. - compile_target_types tt (compile_types (lt)); + compile_target_types tt (compile_types (ot)); auto skip = [&act, &rs] (const target*& pt) { @@ -505,7 +572,10 @@ namespace build2 m = mod ? 2 : 1; } - else if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> ()) + else if (p.is_a<libx> () || + p.is_a<liba> () || + p.is_a<libs> () || + p.is_a<libux> ()) { // Handle imported libraries. // @@ -524,11 +594,11 @@ namespace build2 if (skip (pt)) continue; - // If this is the lib{} target group, then pick the appropriate + // If this is the lib{}/libu{} group, then pick the appropriate // member. // - if (const lib* l = pt->is_a<lib> ()) - pt = &link_member (*l, act, lo); + if (const libx* l = pt->is_a<libx> ()) + pt = &link_member (*l, act, li); } else { @@ -622,8 +692,9 @@ namespace build2 { const target* pt (t.prerequisite_targets[j++]); - if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> () || - p.is_a<bmi> () || p.is_a (tt.bmi)) + if (p.is_a<libx> () || + p.is_a<liba> () || p.is_a<libs> () || p.is_a<libux> () || + p.is_a<bmi> () || p.is_a (tt.bmi)) { ps.emplace_back (p.as_prerequisite ()); } @@ -681,12 +752,12 @@ namespace build2 // Ignore some known target types (fsdir, headers, libraries, // modules). // - if (p1.is_a<fsdir> () || - p1.is_a<lib> () || - p1.is_a<liba> () || p1.is_a<libs> () || - p1.is_a<bmi> () || - p1.is_a<bmie> () || p1.is_a<bmia> () || p1.is_a<bmis> () || - (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) || + if (p1.is_a<fsdir> () || + p1.is_a<libx> () || + p1.is_a<liba> () || p1.is_a<libs> () || p1.is_a<libux> () || + p1.is_a<bmi> () || + p1.is_a<bmie> () || p1.is_a<bmia> () || p1.is_a<bmis> () || + (p.is_a (mod ? *x_mod : x_src) && x_header (p1)) || (p.is_a<c> () && p1.is_a<h> ())) continue; @@ -805,7 +876,7 @@ namespace build2 void link:: append_libraries (strings& args, const file& l, bool la, - const scope& bs, action act, lorder lo) const + const scope& bs, action act, linfo li) const { // Note: lack of the "small function object" optimization will really // kill us here since we are called in a loop. @@ -850,13 +921,13 @@ namespace build2 }; process_libraries ( - act, bs, lo, sys_lib_dirs, l, la, imp, lib, opt, true); + act, bs, li, sys_lib_dirs, l, la, imp, lib, opt, true); } void link:: hash_libraries (sha256& cs, const file& l, bool la, - const scope& bs, action act, lorder lo) const + const scope& bs, action act, linfo li) const { bool win (tclass == "windows"); @@ -896,7 +967,7 @@ namespace build2 }; process_libraries ( - act, bs, lo, sys_lib_dirs, l, la, imp, lib, opt, true); + act, bs, li, sys_lib_dirs, l, la, imp, lib, opt, true); } void link:: @@ -904,7 +975,7 @@ namespace build2 const target& t, const scope& bs, action act, - lorder lo, + linfo li, bool for_install) const { // Use -rpath-link on targets that support it (Linux, *BSD). Note @@ -945,7 +1016,7 @@ namespace build2 auto lib = [&d, this] (const file* l, const string& f, bool sys) { // We don't rpath system libraries. Why, you may ask? There are many - // good reasons and I have them written on an napkin somewhere... + // good reasons and I have them written on a napkin somewhere... // if (sys) return; @@ -1001,13 +1072,14 @@ namespace build2 for (const target* pt: t.prerequisite_targets) { + bool a; const file* f; - const liba* a; - if ((f = a = pt->is_a<liba> ()) || - (f = pt->is_a<libs> ())) + if ((a = (f = pt->is_a<liba> ())) || + (a = (f = pt->is_a<libux> ())) || + ( f = pt->is_a<libs> ())) { - if (!for_install && a == nullptr) + if (!for_install && !a) { // Top-level sharen library dependency. It is either matched or // imported so should be a cc library. @@ -1017,8 +1089,8 @@ namespace build2 "-Wl,-rpath," + f->path ().directory ().string ()); } - process_libraries (act, bs, lo, sys_lib_dirs, - *f, a != nullptr, + process_libraries (act, bs, li, sys_lib_dirs, + *f, a, impf, libf, nullptr); } } @@ -1048,8 +1120,9 @@ namespace build2 const scope& bs (t.base_scope ()); const scope& rs (*bs.root_scope ()); - otype lt (link_type (t)); - lorder lo (link_order (bs, lt)); + ltype lt (link_type (t)); + otype ot (lt.type); + linfo li (link_info (bs, ot)); // Update prerequisites. We determine if any relevant ones render us // out-of-date manually below. @@ -1063,14 +1136,14 @@ namespace build2 path manifest; // Manifest itself (msvc) or compiled object file. timestamp rpath_timestamp (timestamp_nonexistent); // DLLs timestamp. - if (lt == otype::e && tclass == "windows") + if (lt.executable () && tclass == "windows") { // First determine if we need to add our rpath emulating assembly. The // assembly itself is generated later, after updating the target. Omit // it if we are updating for install. // if (!for_install) - rpath_timestamp = windows_rpath_timestamp (t, bs, act, lo); + rpath_timestamp = windows_rpath_timestamp (t, bs, act, li); pair<path, bool> p ( windows_manifest (t, @@ -1182,7 +1255,7 @@ namespace build2 // Then the linker checksum (ar/ranlib or the compiler). // - if (lt == otype::a) + if (lt.static_library ()) { ranlib = rs["bin.ranlib.path"]; @@ -1232,7 +1305,7 @@ namespace build2 string soname1, soname2; strings sargs; - if (lt == otype::a) + if (lt.static_library ()) { if (cid == compiler_id::msvc) ; else @@ -1277,7 +1350,7 @@ namespace build2 { // Set soname. // - if (lt == otype::s) + if (lt.shared_library ()) { const libs_paths& paths (t.data<libs_paths> ()); const string& leaf (paths.effect_soname ().leaf ().string ()); @@ -1315,7 +1388,7 @@ namespace build2 // rpath of the imported libraries (i.e., we assume they are also // installed). But we add -rpath-link for some platforms. // - rpath_libraries (sargs, t, bs, act, lo, for_install); + rpath_libraries (sargs, t, bs, act, li, for_install); if (auto l = t["bin.rpath"]) for (const dir_path& p: cast<dir_paths> (l)) @@ -1350,10 +1423,6 @@ namespace build2 for (const target* pt: t.prerequisite_targets) { - const file* f; - const liba* a (nullptr); - const libs* s (nullptr); - // If this is bmi*{}, then obj*{} is its ad hoc member. // if (modules) @@ -1362,18 +1431,22 @@ namespace build2 pt = pt->member; } + const file* f; + bool a (false), s (false); + if ((f = pt->is_a<obje> ()) || (f = pt->is_a<obja> ()) || (f = pt->is_a<objs> ()) || - (lt != otype::a && - ((f = a = pt->is_a<liba> ()) || - (f = s = pt->is_a<libs> ())))) + (!lt.static_library () && // @@ UTL: TODO libua to liba link. + ((a = (f = pt->is_a<liba> ())) || + (a = (f = pt->is_a<libux> ())) || + (s = (f = pt->is_a<libs> ()))))) { // Link all the dependent interface libraries (shared) or interface // and implementation (static), recursively. // - if (a != nullptr || s != nullptr) - hash_libraries (cs, *f, a != nullptr, bs, act, lo); + if (a || s) + hash_libraries (cs, *f, a, bs, act, li); else cs.append (f->path ().string ()); } @@ -1393,7 +1466,7 @@ namespace build2 // Treat them as inputs, not options. // - if (lt != otype::a) + if (!lt.static_library ()) { hash_options (cs, t, c_libs); hash_options (cs, t, x_libs); @@ -1429,13 +1502,13 @@ namespace build2 path relt (relative (tp)); const process_path* ld (nullptr); - switch (lt) + if (lt.static_library ()) { - case otype::a: - { - ld = &cast<process_path> (rs["bin.ar.path"]); + ld = &cast<process_path> (rs["bin.ar.path"]); - if (cid == compiler_id::msvc) + switch (cid) + { + case compiler_id::msvc: { // lib.exe has /LIBPATH but it's not clear/documented what it's // used for. Perhaps for link-time code generation (/LTCG)? If @@ -1449,25 +1522,30 @@ namespace build2 out = "/OUT:" + relt.string (); args.push_back (out.c_str ()); + break; } - else + default: + { args.push_back (relt.string ().c_str ()); - - break; + break; + } } - // The options are usually similar enough to handle them together. + } + else + { + // The options are usually similar enough to handle executables + // and shared libraries together. // - case otype::e: - case otype::s: + switch (cid) { - if (cid == compiler_id::msvc) + case compiler_id::msvc: { // Using link.exe directly. // ld = &cast<process_path> (rs["bin.ld.path"]); args.push_back ("/NOLOGO"); - if (lt == otype::s) + if (ot == otype::s) args.push_back ("/DLL"); // Add /MACHINE. @@ -1519,7 +1597,7 @@ namespace build2 args.push_back (out3.c_str ()); } - if (lt == otype::s) + if (ot == otype::s) { // On Windows libs{} is the DLL and its first ad hoc group // member is the import library. @@ -1539,7 +1617,7 @@ namespace build2 if (find_option ("/DEBUG", args, true)) { auto& pdb ( - (lt == otype::e ? t.member : t.member->member)->as<file> ()); + (ot == otype::e ? t.member : t.member->member)->as<file> ()); out1 = "/PDB:" + relative (pdb.path ()).string (); args.push_back (out1.c_str ()); } @@ -1553,15 +1631,16 @@ namespace build2 // out = "/OUT:" + relt.string (); args.push_back (out.c_str ()); + break; } - else + default: { ld = &cpath; // Add the option that triggers building a shared library and take // care of any extras (e.g., import library). // - if (lt == otype::s) + if (ot == otype::s) { if (tclass == "macos") args.push_back ("-dynamiclib"); @@ -1581,9 +1660,8 @@ namespace build2 args.push_back ("-o"); args.push_back (relt.string ().c_str ()); + break; } - - break; } } @@ -1593,28 +1671,28 @@ namespace build2 // for (const target* pt: t.prerequisite_targets) { - const file* f; - const liba* a (nullptr); - const libs* s (nullptr); - if (modules) { if (pt->is_a<bmie> () || pt->is_a<bmia> () || pt->is_a<bmis> ()) pt = pt->member; } + const file* f; + bool a (false), s (false); + if ((f = pt->is_a<obje> ()) || (f = pt->is_a<obja> ()) || (f = pt->is_a<objs> ()) || - (lt != otype::a && - ((f = a = pt->is_a<liba> ()) || - (f = s = pt->is_a<libs> ())))) + (!lt.static_library () && // @@ UTL: TODO libua to liba link. + ((a = (f = pt->is_a<liba> ())) || + (a = (f = pt->is_a<libux> ())) || + (s = (f = pt->is_a<libs> ()))))) { // Link all the dependent interface libraries (shared) or interface // and implementation (static), recursively. // - if (a != nullptr || s != nullptr) - append_libraries (sargs, *f, a != nullptr, bs, act, lo); + if (a || s) + append_libraries (sargs, *f, a, bs, act, li); else sargs.push_back (relative (f->path ()).string ()); // string()&& } @@ -1631,7 +1709,7 @@ namespace build2 for (const string& a: sargs) args.push_back (a.c_str ()); - if (lt != otype::a) + if (!lt.static_library ()) { append_options (args, t, c_libs); append_options (args, t, x_libs); @@ -1641,7 +1719,7 @@ namespace build2 // Cleanup old (versioned) libraries. // - if (lt == otype::s) + if (lt.shared_library ()) { const libs_paths& paths (t.data<libs_paths> ()); const path& p (paths.clean); @@ -1697,7 +1775,7 @@ namespace build2 // something like this) we are going to redirect stdout to stderr. For // sane compilers this should be harmless. // - bool filter (cid == compiler_id::msvc && lt != otype::a); + bool filter (cid == compiler_id::msvc && !lt.static_library ()); process pr (*ld, args.data (), 0, (filter ? -1 : 2)); @@ -1708,7 +1786,7 @@ namespace build2 ifdstream is ( move (pr.in_ofd), fdstream_mode::text, ifdstream::badbit); - msvc_filter_link (is, t, lt); + 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 @@ -1780,13 +1858,13 @@ namespace build2 // For Windows generate rpath-emulating assembly (unless updaing for // install). // - if (lt == otype::e && !for_install) - windows_rpath_assembly (t, bs, act, lo, + if (lt.executable () && !for_install) + windows_rpath_assembly (t, bs, act, li, cast<string> (rs[x_target_cpu]), rpath_timestamp, scratch); } - else if (lt == otype::s) + else if (lt.shared_library ()) { // For shared libraries we may need to create a bunch of symlinks. // @@ -1835,56 +1913,49 @@ namespace build2 perform_clean (action act, const target& xt) const { const file& t (xt.as<file> ()); + ltype lt (link_type (t)); - switch (link_type (t)) + if (lt.executable ()) { - case otype::a: - break; // Default. - case otype::e: - { - if (tclass == "windows") - { - if (tsys == "mingw32") - return clean_extra ( - act, t, {".d", ".dlls/", ".manifest.o", ".manifest"}); - else - // Assuming it's VC or alike. Clean up .ilk in case the user - // enabled incremental linking (note that .ilk replaces .exe). - // - return clean_extra ( - act, t, {".d", ".dlls/", ".manifest", "-.ilk"}); - } - - break; - } - case otype::s: + if (tclass == "windows") { - if (tclass == "windows") - { - // Assuming it's VC or alike. Clean up .exp and .ilk. - // - // Note that .exp is based on the .lib, not .dll name. And with - // versioning their bases may not be the same. - // - if (tsys != "mingw32") - return clean_extra (act, t, {{".d", "-.ilk"}, {"-.exp"}}); - } + if (tsys == "mingw32") + return clean_extra ( + act, t, {".d", ".dlls/", ".manifest.o", ".manifest"}); else - { - // Here we can have a bunch of symlinks that we need to remove. If - // the paths are empty, then they will be ignored. + // Assuming it's VC or alike. Clean up .ilk in case the user + // enabled incremental linking (note that .ilk replaces .exe). // - const libs_paths& paths (t.data<libs_paths> ()); - - return clean_extra (act, t, {".d", - paths.link.string ().c_str (), - paths.soname.string ().c_str (), - paths.interm.string ().c_str ()}); - } + return clean_extra ( + act, t, {".d", ".dlls/", ".manifest", "-.ilk"}); + } + } + else if (lt.shared_library ()) + { + if (tclass == "windows") + { + // Assuming it's VC or alike. Clean up .exp and .ilk. + // + // Note that .exp is based on the .lib, not .dll name. And with + // versioning their bases may not be the same. + // + if (tsys != "mingw32") + return clean_extra (act, t, {{".d", "-.ilk"}, {"-.exp"}}); + } + else + { + // Here we can have a bunch of symlinks that we need to remove. If + // the paths are empty, then they will be ignored. + // + const libs_paths& paths (t.data<libs_paths> ()); - break; + return clean_extra (act, t, {".d", + paths.link.string ().c_str (), + paths.soname.string ().c_str (), + paths.interm.string ().c_str ()}); } } + // For static library it's just the defaults. return clean_extra (act, t, {".d"}); } |