From 89a9f8174ec858bf6df8515a84f061f211dec551 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 19 Jul 2016 17:10:51 +0200 Subject: Add import library target libi{}, make libs{} the DLL In the end, having libs{} be the DLL with import library being its member is more natural than making libs{} the import library and having dll{} as its member. --- build2/cxx/compile.cxx | 4 +- build2/cxx/link.cxx | 201 ++++++++++++++++++++++++++++--------------- build2/cxx/module.cxx | 16 ---- build2/cxx/msvc.cxx | 17 +++- build2/cxx/windows-rpath.cxx | 65 +++++++------- 5 files changed, 183 insertions(+), 120 deletions(-) (limited to 'build2/cxx') diff --git a/build2/cxx/compile.cxx b/build2/cxx/compile.cxx index 9efa0b6..432b485 100644 --- a/build2/cxx/compile.cxx +++ b/build2/cxx/compile.cxx @@ -67,7 +67,7 @@ namespace build2 { tracer trace ("cxx::compile"); - path_target& t (static_cast (xt)); + file& t (static_cast (xt)); scope& bs (t.base_scope ()); scope& rs (*bs.root_scope ()); @@ -1235,7 +1235,7 @@ namespace build2 target_state compile:: perform_update (action a, target& xt) { - path_target& t (static_cast (xt)); + file& t (static_cast (xt)); cxx* s (execute_prerequisites (a, t, t.mtime ())); if (s == nullptr) diff --git a/build2/cxx/link.cxx b/build2/cxx/link.cxx index e13cfe3..b80fc8b 100644 --- a/build2/cxx/link.cxx +++ b/build2/cxx/link.cxx @@ -202,6 +202,10 @@ namespace build2 { tracer trace ("cxx::link::search_library"); + // @@ This is hairy enough to warrant a separate implementation for + // Windows. + // + // First check the cache. // if (p.target != nullptr) @@ -210,6 +214,7 @@ namespace build2 scope& rs (*p.scope.root_scope ()); const string& cid (cast (rs["cxx.id"])); const string& tsys (cast (rs["cxx.target.system"])); + const string& tclass (cast (rs["cxx.target.class"])); bool l (p.is_a ()); const string* ext (l ? nullptr : p.ext); // Only for liba/libs. @@ -321,7 +326,48 @@ namespace build2 f /= sn; mt = file_mtime (f); - if (tsys == "mingw32") + if (mt != timestamp_nonexistent) + { + // On Windows what we found is the import library which we need + // to make the first ad hoc member of libs{}. + // + if (tclass == "windows") + { + s = &targets.insert ( + d, dir_path (), p.name, nullptr, trace); + + if (s->member == nullptr) + { + libi& i ( + targets.insert ( + d, dir_path (), p.name, se, trace)); + + if (i.path ().empty ()) + i.path (move (f)); + + i.mtime (mt); + + // Presumably there is a DLL somewhere, we just don't know + // where (and its possible we might have to look for one if we + // decide we need to do rpath emulation for installed + // libraries as well). We will represent this as empty path + // but valid timestamp (aka "trust me, it's there"). + // + s->mtime (mt); + s->member = &i; + } + } + else + { + s = &targets.insert (d, dir_path (), p.name, se, trace); + + if (s->path ().empty ()) + s->path (move (f)); + + s->mtime (mt); + } + } + else if (ext == nullptr && tsys == "mingw32") { // Above we searched for the import library (.dll.a) but if it's // not found, then we also search for the .dll (unless the @@ -329,22 +375,19 @@ namespace build2 // directly. Note also that the resulting libs{} would end up // being the .dll. // - if (mt == timestamp_nonexistent && ext == nullptr) - { - se = &extension_pool.find ("dll"); - f = f.base (); // Remove .a from .dll.a. - mt = file_mtime (f); - } - } + se = &extension_pool.find ("dll"); + f = f.base (); // Remove .a from .dll.a. + mt = file_mtime (f); - if (mt != timestamp_nonexistent) - { - s = &targets.insert (d, dir_path (), p.name, se, trace); + if (mt != timestamp_nonexistent) + { + s = &targets.insert (d, dir_path (), p.name, se, trace); - if (s->path ().empty ()) - s->path (move (f)); + if (s->path ().empty ()) + s->path (move (f)); - s->mtime (mt); + s->mtime (mt); + } } } @@ -595,7 +638,7 @@ namespace build2 { tracer trace ("cxx::link::apply"); - path_target& t (static_cast (xt)); + file& t (static_cast (xt)); scope& bs (t.base_scope ()); scope& rs (*bs.root_scope ()); @@ -656,28 +699,13 @@ namespace build2 else if (tclass == "windows") { // On Windows libs{} is an ad hoc group. The libs{} itself is - // the import library and we add dll{} as a member (see below). - // While at first it may seem strange that libs{} is the import - // library and not the DLL, if you meditate on it, you will see - // it makes a lot of sense: our main task here is building and - // for that we need the import library, not the DLL. + // the DLL and we add libi{} import library as its member (see + // below). // if (tsys == "mingw32") - { p = "lib"; - e = "dll.a"; - } - else - { - // Usually on Windows the import library is called the same as - // the DLL but with the .lib extension. Which means it clashes - // with the static library. Instead of decorating the static - // library name with ugly suffixes (as is customary), let's - // use the MinGW approach (one must admit it's quite elegant) - // and call it .dll.lib. - // - e = "dll.lib"; - } + + e = "dll"; } else { @@ -713,14 +741,21 @@ namespace build2 if (tclass == "windows") { - // DLL + // Import library. // if (lt == otype::s) { - file& dll (add_adhoc (t, "dll")); - - if (dll.path ().empty ()) - dll.derive_path ("dll", tsys == "mingw32" ? "lib" : nullptr); + file& imp (add_adhoc (t, "libi")); + + // Usually on Windows the import library is called the same as the + // DLL but with the .lib extension. Which means it clashes with the + // static library. Instead of decorating the static library name + // with ugly suffixes (as is customary), let's use the MinGW + // approach (one must admit it's quite elegant) and call it + // .dll.lib. + // + if (imp.path ().empty ()) + imp.derive_path (t.path (), tsys == "mingw32" ? "a" : "lib"); } // PDB @@ -729,10 +764,13 @@ namespace build2 cid == "msvc" && find_option ("/DEBUG", t, "cxx.loptions", true)) { - // Add after the DLL if any. + // Add after the import library if any. // file& pdb (add_adhoc (t.member == nullptr ? t : *t.member, "pdb")); + // We call it foo.{exe,dll}.pdb rather than just foo.pdb because we + // can have both foo.exe and foo.dll in the same directory. + // if (pdb.path ().empty ()) pdb.derive_path (t.path (), "pdb"); } @@ -1360,17 +1398,28 @@ namespace build2 for (target* pt: t.prerequisite_targets) { - path_target* ppt; + file* f; liba* a (nullptr); + libs* s (nullptr); - if ((ppt = pt->is_a ()) || - (ppt = pt->is_a ()) || - (ppt = pt->is_a ()) || + if ((f = pt->is_a ()) || + (f = pt->is_a ()) || + (f = pt->is_a ()) || (lt != otype::a && - ((ppt = a = pt->is_a ()) || - (ppt = pt->is_a ())))) + ((f = a = pt->is_a ()) || + (f = s = pt->is_a ())))) { - cs.append (ppt->path ().string ()); + // On Windows a shared library is a DLL with the import library as + // a first ad hoc group member. MinGW though can link directly to + // DLLs (see search_library() for details). + // + if (s != nullptr && tclass == "windows") + { + if (s->member != nullptr) + f = static_cast (s->member); + } + + cs.append (f->path ().string ()); // If this is a static library, link all the libraries it depends // on, recursively. @@ -1487,26 +1536,26 @@ namespace build2 if (lt == otype::s) { - // On Windows libs{} is the import stub and its first ad hoc - // group member is dll{}. + // On Windows libs{} is the DLL and its first ad hoc group + // member is the import library. // // This will also create the .exp export file. Its name will be // derived from the import library by changing the extension. - // Lucky us -- there is no option to name it. + // Lucky for us -- there is no option to name it. // - out2 = "/IMPLIB:" + relt.string (); + auto imp (static_cast (t.member)); + out2 = "/IMPLIB:" + relative (imp->path ()).string (); args.push_back (out2.c_str ()); - - relt = relative (static_cast (t.member)->path ()); } - // If we have /DEBUG then name the .pdb file. We call it - // foo.{exe,dll}.pdb rather than just foo.pdb because we can have, - // both foo.exe and foo.dll in the same directory. + // If we have /DEBUG then name the .pdb file. It is either the + // first (exe) or the second (dll) ad hoc group member. // if (find_option ("/DEBUG", args, true)) { - out1 = "/PDB:" + relt.string () + ".pdb"; + auto pdb (static_cast ( + lt == otype::e ? t.member : t.member->member)); + out1 = "/PDB:" + relative (pdb->path ()).string (); args.push_back (out1.c_str ()); } @@ -1533,13 +1582,12 @@ namespace build2 if (tsys == "mingw32") { - // On Windows libs{} is the import stub and its first ad hoc - // group member is dll{}. + // On Windows libs{} is the DLL and its first ad hoc group + // member is the import library. // - out = "-Wl,--out-implib=" + relt.string (); + auto imp (static_cast (t.member)); + out = "-Wl,--out-implib=" + relative (imp->path ()).string (); args.push_back (out.c_str ()); - - relt = relative (static_cast (t.member)->path ()); } } @@ -1553,17 +1601,28 @@ namespace build2 for (target* pt: t.prerequisite_targets) { - path_target* ppt; + file* f; liba* a (nullptr); + libs* s (nullptr); - if ((ppt = pt->is_a ()) || - (ppt = pt->is_a ()) || - (ppt = pt->is_a ()) || + if ((f = pt->is_a ()) || + (f = pt->is_a ()) || + (f = pt->is_a ()) || (lt != otype::a && - ((ppt = a = pt->is_a ()) || - (ppt = pt->is_a ())))) + ((f = a = pt->is_a ()) || + (f = s = pt->is_a ())))) { - sargs.push_back (relative (ppt->path ()).string ()); // string()&& + // On Windows a shared library is a DLL with the import library as a + // first ad hoc group member. MinGW though can link directly to DLLs + // (see search_library() for details). + // + if (s != nullptr && tclass == "windows") + { + if (s->member != nullptr) + f = static_cast (s->member); + } + + sargs.push_back (relative (f->path ()).string ()); // string()&& // If this is a static library, link all the libraries it depends // on, recursively. @@ -1687,7 +1746,7 @@ namespace build2 { case otype::a: { - e = {"+.d"}; + e = {".d"}; break; } case otype::e: @@ -1717,7 +1776,7 @@ namespace build2 { // Assuming it's VC or alike. Clean up .exp and .ilk. // - e = {".d", "-.exp", "--.ilk"}; + e = {".d", ".exp", "-.ilk"}; } else e = {".d"}; diff --git a/build2/cxx/module.cxx b/build2/cxx/module.cxx index 69e76a3..8972bf9 100644 --- a/build2/cxx/module.cxx +++ b/build2/cxx/module.cxx @@ -258,7 +258,6 @@ namespace build2 const string& cid (cast (r["cxx.id"])); const string& tsys (cast (r["cxx.target.system"])); - const string& tclass (cast (r["cxx.target.class"])); // Initialize the bin module. Only do this if it hasn't already been // loaded so that we don't overwrite user's bin.* settings. @@ -361,21 +360,6 @@ namespace build2 install_path (b, dir_path ("include")); install_path (b, dir_path ("include")); - // Create additional target types for certain target platforms. - // - if (tclass == "windows") - { - const target_type& dll (b.derive_target_type ("dll").first); - install_path (dll, b, dir_path ("bin")); - - if (cid == "msvc") - { - const target_type& pdb (b.derive_target_type ("pdb").first); - install_path (pdb, b, dir_path ("bin")); - install_mode (pdb, b, "644"); - } - } - return true; } } diff --git a/build2/cxx/msvc.cxx b/build2/cxx/msvc.cxx index 1c5a3fa..ff91018 100644 --- a/build2/cxx/msvc.cxx +++ b/build2/cxx/msvc.cxx @@ -227,11 +227,24 @@ namespace build2 libs* msvc_search_shared (const path& ld, const dir_path& d, prerequisite& p) { + tracer trace ("cxx::msvc_search_shared"); + libs* r (nullptr); - auto search = [&r, &ld, &d, &p] (const char* pf, const char* sf) -> bool + auto search = [&r, &ld, &d, &p, &trace] ( + const char* pf, const char* sf) -> bool { - r = search_library (ld, d, p, otype::s, pf, sf); + if (libi* i = search_library (ld, d, p, otype::s, pf, sf)) + { + r = &targets.insert (d, dir_path (), p.name, nullptr, trace); + + if (r->member == nullptr) + { + r->mtime (i->mtime ()); + r->member = i; + } + } + return r != nullptr; }; diff --git a/build2/cxx/windows-rpath.cxx b/build2/cxx/windows-rpath.cxx index 0bd4bc5..b5bad5e 100644 --- a/build2/cxx/windows-rpath.cxx +++ b/build2/cxx/windows-rpath.cxx @@ -51,22 +51,18 @@ namespace build2 { if (libs* ls = pt->is_a ()) { - // This can be an installed library in which case we will have just - // the import stub but may also have just the DLL. For now we don't - // bother with installed libraries. + // Skip installed DLLs. // - if (ls->member == nullptr) + if (ls->path ().empty ()) continue; - file& dll (static_cast (*ls->member)); - // What if the DLL is in the same directory as the executable, will // it still be found even if there is an assembly? On the other // hand, handling it as any other won't hurt us much. // timestamp t; - if ((t = dll.mtime ()) > r) + if ((t = ls->mtime ()) > r) r = t; if ((t = windows_rpath_timestamp (*ls)) > r) @@ -80,18 +76,18 @@ namespace build2 // Like *_timestamp() but actually collect the DLLs. // static void - rpath_dlls (set& s, file& t) + rpath_dlls (set& s, file& t) { for (target* pt: t.prerequisite_targets) { if (libs* ls = pt->is_a ()) { - if (ls->member == nullptr) + // Skip installed DLLs. + // + if (ls->path ().empty ()) continue; - file& dll (static_cast (*ls->member)); - - s.insert (&dll); + s.insert (ls); rpath_dlls (s, *ls); } } @@ -143,7 +139,7 @@ namespace build2 // bool empty (ts == timestamp_nonexistent); - set dlls; + set dlls; if (!empty) rpath_dlls (dlls, t); @@ -185,16 +181,12 @@ namespace build2 scope& as (*rs.weak_scope ()); // Amalgamation scope. - for (file* dt: dlls) + auto link = [&as, &ad] (const path& f, const path& l) { - const path& dp (dt->path ()); // DLL path. - const path dn (dp.leaf ()); // DLL name. - const path lp (ad / dn); // Link path. - - auto print = [&dp, &lp] (const char* cmd) + auto print = [&f, &l] (const char* cmd) { if (verb >= 3) - text << cmd << ' ' << dp << ' ' << lp; + text << cmd << ' ' << f << ' ' << l; }; // First we try to create a symlink. If that fails (e.g., "Windows @@ -208,10 +200,10 @@ namespace build2 // part of the same amalgamation. This way if the amalgamation is // moved as a whole, the links will remain valid. // - if (dp.sub (as.out_path ())) - mksymlink (dp.relative (ad), lp); + if (f.sub (as.out_path ())) + mksymlink (f.relative (ad), l); else - mksymlink (dp, lp); + mksymlink (f, l); print ("ln -s"); } @@ -222,12 +214,12 @@ namespace build2 if (c != EPERM && c != ENOSYS) { print ("ln -s"); - fail << "unable to create symlink " << lp << ": " << e.what (); + fail << "unable to create symlink " << l << ": " << e.what (); } try { - mkhardlink (dp, lp); + mkhardlink (f, l); print ("ln"); } catch (const system_error& e) @@ -237,23 +229,38 @@ namespace build2 if (c != EPERM && c != ENOSYS) { print ("ln"); - fail << "unable to create hard link " << lp << ": " - << e.what (); + fail << "unable to create hardlink " << l << ": " << e.what (); } try { - cpfile (dp, lp); + cpfile (f, l); print ("cp"); } catch (const system_error& e) { print ("cp"); - fail << "unable to create copy " << lp << ": " << e.what (); + fail << "unable to create copy " << l << ": " << e.what (); } } } + }; + + for (libs* dll: dlls) + { + const path& dp (dll->path ()); // DLL path. + const path dn (dp.leaf ()); // DLL name. + link (dp, ad / dn); + + // Link .pdb if there is one (second member of the ad hoc group). + // + if (dll->member != nullptr && dll->member->member != nullptr) + { + file& pdb (static_cast (*dll->member->member)); + link (pdb.path (), ad / pdb.path ().leaf ()); + } + ofs << " \n"; } -- cgit v1.1