diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2018-09-04 16:10:21 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2018-09-04 16:14:08 +0200 |
commit | 3fd36c27e9455dae10ed4f569ca4362219bbcbcb (patch) | |
tree | 8dd42450651d18fbe463001f19ed04f2ed66d180 | |
parent | 5007870b52aa549971824959a55ad3bb886f09e0 (diff) |
Initial work on binless (binary-less aka header-only) library support
-rw-r--r-- | build2/algorithm.cxx | 59 | ||||
-rw-r--r-- | build2/algorithm.hxx | 3 | ||||
-rw-r--r-- | build2/cc/common.hxx | 3 | ||||
-rw-r--r-- | build2/cc/install-rule.cxx | 17 | ||||
-rw-r--r-- | build2/cc/link-rule.cxx | 620 | ||||
-rw-r--r-- | build2/cc/link-rule.hxx | 4 | ||||
-rw-r--r-- | build2/cc/pkgconfig.cxx | 123 | ||||
-rw-r--r-- | build2/cc/windows-rpath.cxx | 4 | ||||
-rw-r--r-- | build2/install/rule.cxx | 35 |
9 files changed, 514 insertions, 354 deletions
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx index bfe6943..ef33cbe 100644 --- a/build2/algorithm.cxx +++ b/build2/algorithm.cxx @@ -1952,10 +1952,18 @@ namespace build2 } }; - auto ei (extra.begin ()), ee (extra.end ()); + const path& fp (ft.path ()); + auto ei (extra.begin ()), ee (extra.end ()); if (ei != ee) - clean_extra (ft, nullptr, *ei++); + { + if (!fp.empty ()) + clean_extra (ft, nullptr, *ei); + + ++ei; + } + + target_state tr (target_state::unchanged); // Check if we were asked not to actually remove the files. The extras are // tricky: some of them, like depdb should definitely be removed. But @@ -1970,31 +1978,44 @@ namespace build2 // for (const target* m (ft.member); m != nullptr; m = m->member) { - const file* fm (m->is_a<file> ()); - const path* fp (fm != nullptr ? &fm->path () : nullptr); + const file* mf (m->is_a<file> ()); + const path* mp (mf != nullptr ? &mf->path () : nullptr); - if (fm == nullptr || fp->empty ()) + if (mf == nullptr || mp->empty ()) continue; if (ei != ee) - clean_extra (*fm, fp, *ei++); + clean_extra (*mf, mp, *ei++); + + if (!clean) + continue; - target_state r (clean && rmfile (*fp, 3) - ? target_state::changed - : target_state::unchanged); + // Make this "primary target" for diagnostics/result purposes if the + // primary target is unreal. + // + if (fp.empty ()) + { + if (rmfile (*mp, *mf)) + tr = target_state::changed; + } + else + { + target_state r (rmfile (*mp, 3) + ? target_state::changed + : target_state::unchanged); - if (r == target_state::changed && ep.empty ()) - ep = *fp; + if (r == target_state::changed && ep.empty ()) + ep = *mp; - er |= r; + er |= r; + } } // Now clean the primary target and its prerequisited in the reverse order // of update: first remove the file, then clean the prerequisites. // - target_state tr (clean && rmfile (ft.path (), ft) // Path must be assigned. - ? target_state::changed - : target_state::unchanged); + if (clean && !fp.empty () && rmfile (fp, ft)) + tr = target_state::changed; // Update timestamp in case there are operations after us that could use // the information. @@ -2032,13 +2053,17 @@ namespace build2 target_state perform_clean (action a, const target& t) { - return clean_extra (a, t.as<file> (), {nullptr}); + const file& f (t.as<file> ()); + assert (!f.path ().empty ()); + return clean_extra (a, f, {nullptr}); } target_state perform_clean_depdb (action a, const target& t) { - return clean_extra (a, t.as<file> (), {".d"}); + const file& f (t.as<file> ()); + assert (!f.path ().empty ()); + return clean_extra (a, f, {".d"}); } target_state diff --git a/build2/algorithm.hxx b/build2/algorithm.hxx index 430cc9f..e908df8 100644 --- a/build2/algorithm.hxx +++ b/build2/algorithm.hxx @@ -656,6 +656,9 @@ namespace build2 // // You can also clean extra files derived from ad hoc group members. // + // Note that if the target path is empty then it is assumed "unreal" and + // is not cleaned (but its prerequisites/members still are). + // target_state clean_extra (action, const file&, initializer_list<initializer_list<const char*>> extra); diff --git a/build2/cc/common.hxx b/build2/cc/common.hxx index d809050..952e383 100644 --- a/build2/cc/common.hxx +++ b/build2/cc/common.hxx @@ -144,7 +144,8 @@ namespace build2 const target_type* x_mod; // Module target type (mxx{}), if any. // Array of target types that are considered headers. Keep them in the - // most likely to appear order and terminate with NULL. + // most likely to appear order with the "header header" first and + // terminated with NULL. // const target_type* const* x_hdr; diff --git a/build2/cc/install-rule.cxx b/build2/cc/install-rule.cxx index d687903..b2b508e 100644 --- a/build2/cc/install-rule.cxx +++ b/build2/cc/install-rule.cxx @@ -174,7 +174,7 @@ namespace build2 else // install or uninstall { // Derive shared library paths and cache them in the target's aux - // storage if we are un/installing (used in *_extra() functions + // storage if we are un/installing (used in the *_extra() functions // below). // static_assert (sizeof (link_rule::libs_paths) <= target::data_size, @@ -183,12 +183,15 @@ namespace build2 file* f; if ((f = t.is_a<libs> ()) != nullptr && tclass != "windows") { - const string* p (cast_null<string> (t["bin.lib.prefix"])); - const string* s (cast_null<string> (t["bin.lib.suffix"])); - t.data ( - link_.derive_libs_paths (*f, - p != nullptr ? p->c_str (): nullptr, - s != nullptr ? s->c_str (): nullptr)); + if (!f->path ().empty ()) // Not binless. + { + const string* p (cast_null<string> (t["bin.lib.prefix"])); + const string* s (cast_null<string> (t["bin.lib.suffix"])); + t.data ( + link_.derive_libs_paths (*f, + p != nullptr ? p->c_str (): nullptr, + s != nullptr ? s->c_str (): nullptr)); + } } } diff --git a/build2/cc/link-rule.cxx b/build2/cc/link-rule.cxx index 5ebb97a..b644174 100644 --- a/build2/cc/link-rule.cxx +++ b/build2/cc/link-rule.cxx @@ -82,11 +82,14 @@ namespace build2 if (include (a, t, p) != include_type::normal) continue; - if (p.is_a (x_src) || (x_mod != nullptr && p.is_a (*x_mod))) + if (p.is_a (x_src) || + (lt.library () && p.is_a (*x_hdr[0])) || // Header-only library. + (x_mod != nullptr && p.is_a (*x_mod))) { seen_x = seen_x || true; } - else if (p.is_a<c> ()) + else if (p.is_a<c> () || + (lt.library () && p.is_a<h> ())) // Header-only library. { seen_c = seen_c || true; } @@ -297,6 +300,41 @@ namespace build2 return libs_paths {move (lk), 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. + // + const libx* ul; + if ((ul = pt->is_a<libul> ()) || + (ul = pt->is_a<libu> ())) + { + pf = &link_member (*ul, a, li).as<file> (); + } + else if ((pf = pt->is_a<libue> ()) || + (pf = pt->is_a<libus> ()) || + (pf = pt->is_a<libua> ())) + ; + else + continue; + + if (!pf->path ().empty () || find_binfull (a, *pf, li)) + return true; + } + + return false; + }; + recipe link_rule:: apply (action a, target& xt) const { @@ -321,218 +359,11 @@ namespace build2 if (lt.library ()) t.vars.assign (c_type) = string (x); - // Derive file name(s) and add ad hoc group members. - // - { - target_lock libi; // Have to hold until after PDB member addition. - - 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; - } - } - } - - t.derive_path (e, p, s); - } - else - { - if (auto l = t[ot == otype::e ? "bin.exe.prefix" : "bin.lib.prefix"]) - p = cast<string> (l).c_str (); - if (auto l = t[ot == otype::e ? "bin.exe.suffix" : "bin.lib.suffix"]) - s = cast<string> (l).c_str (); - - switch (ot) - { - case otype::e: - { - if (tclass == "windows") - e = "exe"; - else - e = ""; + bool binless (lt.library ()); // Binary-less until proven otherwise. - t.derive_path (e, p, s); - break; - } - case otype::a: - { - if (tsys == "win32-msvc") - e = "lib"; - else - { - if (p == nullptr) p = "lib"; - e = "a"; - } - - 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_member<bin::libi> (a, t); - - md.libs_data = derive_libs_paths (t, p, s); - - if (libi) - match_recipe (libi, group_recipe); // Set recipe and unlock. - - break; - } - } - - // Add VC's .pdb. Note that we are looking for the link.exe /DEBUG - // option. - // - if (ot != otype::a && - tsys == "win32-msvc" && - (find_option ("/DEBUG", t, c_loptions, true) || - find_option ("/DEBUG", t, x_loptions, true))) - { - // Note: add after the import library if any. - // - target_lock pdb ( - add_adhoc_member (a, t, *bs.find_target_type ("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. - // - pdb.target->as<file> ().derive_path (t.path (), "pdb"); - - match_recipe (pdb, group_recipe); // Set recipe and unlock. - } - - // Add pkg-config's .pc file. - // - // Note that we do it here regardless of whether we are installing - // or not for two reasons. Firstly, it is not easy to detect this - // situation in apply() since the for-install hasn't yet been - // communicated by install_rule. Secondly, always having the member - // takes care of cleanup automagically. The actual generation - // happens in perform_update() below. - // - if (ot != otype::e) - { - target_lock pc ( - add_adhoc_member ( - a, 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 refere to the library. This - // is also the reason why we use the static/shared suffixes rather - // that a./.lib/.so/.dylib/.dll. - // - pc.target->as<file> ().derive_path (nullptr, - (p == nullptr ? "lib" : p), - s); - - match_recipe (pc, group_recipe); // Set recipe and unlock. - } - - // 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). - // - target_lock dir ( - add_adhoc_member ( - a, - t, - fsdir::static_type, - path_cast<dir_path> (t.path () + ".dlls"), - t.out, - string ())); - - // 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 on 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.target->assign (var_backlink) = "copy"; -#endif - match_recipe (dir, group_recipe); // Set recipe and unlock. - } - } - } - - // Inject dependency on the output directory. + // 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); @@ -540,6 +371,11 @@ namespace build2 // 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 @@ -595,6 +431,8 @@ namespace build2 if (mod || p.is_a (x_src) || p.is_a<c> ()) { + binless = binless && false; + // Rule chaining, part 1. // @@ -730,6 +568,8 @@ namespace build2 continue; } + binless = binless && !(pt->is_a<objx> () || pt->is_a<bmix> ()); + m = 3; } @@ -740,6 +580,237 @@ namespace build2 // 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. + // + { + target_lock libi; // Have to hold until after PDB member addition. + + const char* e (nullptr); // Extension. + const char* p (nullptr); // Prefix. + const char* s (nullptr); // Suffix. + + if (lt.utility) + { + // These are all static libraries with names indicating the kind of + // object files they contain (similar to how we name object files + // themselves). We add the 'u' extension to avoid clashes with + // real libraries/import stubs. + // + // libue libhello.u.a hello.exe.u.lib + // libua libhello.a.u.a hello.lib.u.lib + // libus libhello.so.u.a hello.dll.u.lib hello.dylib.u.lib + // + // Note that we currently don't add bin.lib.{prefix,suffix} since + // these are not installed. + // + if (tsys == "win32-msvc") + { + switch (ot) + { + case otype::e: e = "exe.u.lib"; break; + case otype::a: e = "lib.u.lib"; break; + case otype::s: e = "dll.u.lib"; break; + } + } + else + { + p = "lib"; + + if (tsys == "mingw32") + { + switch (ot) + { + case otype::e: e = "exe.u.a"; break; + case otype::a: e = "a.u.a"; break; + case otype::s: e = "dll.u.a"; break; + } + + } + else if (tsys == "darwin") + { + switch (ot) + { + case otype::e: e = "u.a"; break; + case otype::a: e = "a.u.a"; break; + case otype::s: e = "dylib.u.a"; break; + } + } + else + { + switch (ot) + { + case otype::e: e = "u.a"; break; + case otype::a: e = "a.u.a"; break; + case otype::s: e = "so.u.a"; break; + } + } + } + + if (binless) + t.path (empty_path); + else + t.derive_path (e, p, s); + } + else + { + if (auto l = t[ot == otype::e ? "bin.exe.prefix" : "bin.lib.prefix"]) + p = cast<string> (l).c_str (); + if (auto l = t[ot == otype::e ? "bin.exe.suffix" : "bin.lib.suffix"]) + s = cast<string> (l).c_str (); + + switch (ot) + { + case otype::e: + { + if (tclass == "windows") + e = "exe"; + else + e = ""; + + t.derive_path (e, p, s); + break; + } + case otype::a: + { + if (tsys == "win32-msvc") + e = "lib"; + else + { + if (p == nullptr) p = "lib"; + e = "a"; + } + + if (binless) + t.path (empty_path); + else + t.derive_path (e, p, s); + + break; + } + case otype::s: + { + if (binless) + t.path (empty_path); + else + { + // On Windows libs{} is an ad hoc group. The libs{} itself is + // the DLL and we add libi{} import library as its member. + // + if (tclass == "windows") + libi = add_adhoc_member<bin::libi> (a, t); + + md.libs_data = derive_libs_paths (t, p, s); + + if (libi) + match_recipe (libi, group_recipe); // Set recipe and unlock. + } + + 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)) + { + // Note: add after the import library if any. + // + target_lock pdb ( + add_adhoc_member (a, t, *bs.find_target_type ("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. + // + pdb.target->as<file> ().derive_path (t.path (), "pdb"); + + match_recipe (pdb, group_recipe); // Set recipe and unlock. + } + } + + // 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) + { + target_lock pc ( + add_adhoc_member ( + a, 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/shared suffixes rather + // that a./.lib/.so/.dylib/.dll. + // + pc.target->as<file> ().derive_path (nullptr, + (p == nullptr ? "lib" : p), + s); + + match_recipe (pc, group_recipe); // Set recipe and unlock. + } + + // 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). + // + target_lock dir ( + add_adhoc_member ( + a, + t, + fsdir::static_type, + path_cast<dir_path> (t.path () + ".dlls"), + t.out, + string ())); + + // 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 on 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.target->assign (var_backlink) = "copy"; +#endif + match_recipe (dir, group_recipe); // Set recipe and unlock. + } + } + } + // 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. @@ -1056,6 +1127,8 @@ namespace build2 } } + md.binless = binless; + switch (a) { case perform_update_id: return [this] (action a, const target& t) @@ -1098,6 +1171,9 @@ namespace build2 } else { + if (l->path ().empty ()) // Binless. + return; + bool lu (l->is_a<libux> ()); // The utility/non-utility case is tricky. Consider these two @@ -1249,6 +1325,9 @@ namespace build2 } else { + if (l->path ().empty ()) // Binless. + return; + bool lu (l->is_a<libux> ()); if (lu) @@ -1368,6 +1447,9 @@ namespace build2 { if (!l->is_a<libs> ()) return; + + if (l->path ().empty ()) // Binless. + return; } else { @@ -1428,12 +1510,16 @@ namespace build2 { if (!for_install && !la) { - // Top-level shared library dependency. It is either matched or - // imported so should be a cc library. + // Top-level shared library dependency. // - if (!cast_false<bool> (f->vars[c_system])) - args.push_back ( - "-Wl,-rpath," + f->path ().directory ().string ()); + if (!f->path ().empty ()) // Not binless. + { + // It is either matched or imported so should be a cc library. + // + if (!cast_false<bool> (f->vars[c_system])) + args.push_back ( + "-Wl,-rpath," + f->path ().directory ().string ()); + } } process_libraries (a, bs, li, sys_lib_dirs, @@ -1478,15 +1564,41 @@ namespace build2 otype ot (lt.type); linfo li (link_info (bs, ot)); + bool binless (md.binless); + assert (ot != otype::e || !binless); // Sanity check. + // Update prerequisites. We determine if any relevant ones render us // out-of-date manually below. // // Note that straight_execute_prerequisites() will blank out all the ad // hoc prerequisites so we don't need to worry about them from now on. // + target_state ts (straight_execute_prerequisites (a, t)); + + // (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; + } + + // Determine if we are out-of-date. + // bool update (false); timestamp mt (t.load_mtime ()); - target_state ts (straight_execute_prerequisites (a, t)); // Open the dependency database (do it before messing with Windows // manifests to diagnose missing output directory). @@ -1925,25 +2037,6 @@ namespace build2 dd.close (); - // (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) - { - bool la; - const file* f; - - if ((la = (f = t.is_a<liba> ())) || - ( f = t.is_a<libs> ())) - pkgconfig_save (a, *f, la); - } - // If nothing changed, then we are done. // if (!update) @@ -2438,33 +2531,42 @@ namespace build2 return clean_extra ( a, t, {".d", ".dlls/", ".manifest", "-.ilk"}); } + // For other platforms it's the defaults. } - else if (lt.shared_library ()) + else { - 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 (a, t, {{".d", "-.ilk"}, {"-.exp"}}); - } - else + const match_data& md (t.data<match_data> ()); + + if (md.binless) + return clean_extra (a, t, {nullptr}); // Clean prerequsites/members. + + if (lt.shared_library ()) { - // 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<match_data> ().libs_data); + 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 (a, 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 (md.libs_data); - return clean_extra (a, t, {".d", - paths.link.string ().c_str (), - paths.soname.string ().c_str (), - paths.interm.string ().c_str ()}); + return clean_extra (a, t, {".d", + paths.link.string ().c_str (), + paths.soname.string ().c_str (), + paths.interm.string ().c_str ()}); + } } + // For static library it's the defaults. } - // For static library it's just the defaults. return clean_extra (a, t, {".d"}); } diff --git a/build2/cc/link-rule.hxx b/build2/cc/link-rule.hxx index ee4a2bd..47bfdf1 100644 --- a/build2/cc/link-rule.hxx +++ b/build2/cc/link-rule.hxx @@ -98,6 +98,8 @@ namespace build2 // optional<bool> for_install; + bool binless; // Binary-less library. + libs_paths libs_data; }; @@ -155,7 +157,7 @@ namespace build2 // pkg-config's .pc file generation (pkgconfig.cxx). // void - pkgconfig_save (action, const file&, bool) const; + pkgconfig_save (action, const file&, bool, bool) const; private: const string rule_id; diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx index 4a8d995..795e5d7 100644 --- a/build2/cc/pkgconfig.cxx +++ b/build2/cc/pkgconfig.cxx @@ -1114,7 +1114,7 @@ namespace build2 #endif void link_rule:: - pkgconfig_save (action a, const file& l, bool la) const + pkgconfig_save (action a, const file& l, bool la, bool binless) const { tracer trace (x, "pkgconfig_save"); @@ -1132,7 +1132,9 @@ namespace build2 using install::resolve_dir; dir_path idir (resolve_dir (l, cast<dir_path> (l["install.include"]))); - dir_path ldir (resolve_dir (l, cast<dir_path> (l["install.lib"]))); + dir_path ldir (binless + ? dir_path () + : resolve_dir (l, cast<dir_path> (l["install.lib"]))); if (verb >= 2) text << "cat >" << p; @@ -1256,75 +1258,78 @@ namespace build2 // the second position, not first. Gets even trickier with // Libs.private split. // - os << "Libs:"; - os << " -L" << escape (ldir.string ()); - - // Now process ourselves as if we were being linked to something (so - // pretty similar to link_rule::append_libraries()). - // - bool priv (false); - auto imp = [&priv] (const file&, bool la) {return priv && la;}; - - auto lib = [&os, &save_library] (const file* const* c, - const string& p, - lflags, - bool) + if (!binless) { - const file* l (c != nullptr ? *c : nullptr); + os << "Libs:"; + os << " -L" << escape (ldir.string ()); + + // Now process ourselves as if we were being linked to something (so + // pretty similar to link_rule::append_libraries()). + // + bool priv (false); + auto imp = [&priv] (const file&, bool la) {return priv && la;}; - if (l != nullptr) + auto lib = [&os, &save_library] (const file* const* c, + const string& p, + lflags, + bool) { - if (l->is_a<libs> () || l->is_a<liba> ()) // See through libux. - save_library (*l); - } - else - os << ' ' << p; // Something "system'y", pass as is. - }; + const file* l (c != nullptr ? *c : nullptr); - auto opt = [] (const file&, - const string&, - bool, bool) - { - //@@ TODO: should we filter -L similar to -I? - //@@ TODO: how will the Libs/Libs.private work? - //@@ TODO: remember to use escape() + if (l != nullptr) + { + if (l->is_a<libs> () || l->is_a<liba> ()) // See through libux. + save_library (*l); + } + else + os << ' ' << p; // Something "system'y", pass as is. + }; - /* - // If we need an interface value, then use the group (lib{}). - // - if (const target* g = exp && l.is_a<libs> () ? l.group : &l) + auto opt = [] (const file&, + const string&, + bool, bool) { - const variable& var ( - com - ? (exp ? c_export_loptions : c_loptions) - : (t == x - ? (exp ? x_export_loptions : x_loptions) - : var_pool[t + (exp ? ".export.loptions" : ".loptions")])); - - append_options (args, *g, var); - } - */ - }; + //@@ TODO: should we filter -L similar to -I? + //@@ TODO: how will the Libs/Libs.private work? + //@@ TODO: remember to use escape() - // Pretend we are linking an executable using what would be normal, - // system-default link order. - // - linfo li {otype::e, la ? lorder::a_s : lorder::s_a}; - - process_libraries (a, bs, li, sys_lib_dirs, - l, la, 0, // Link flags. - imp, lib, opt, true); - os << endl; + /* + // If we need an interface value, then use the group (lib{}). + // + if (const target* g = exp && l.is_a<libs> () ? l.group : &l) + { + const variable& var ( + com + ? (exp ? c_export_loptions : c_loptions) + : (t == x + ? (exp ? x_export_loptions : x_loptions) + : var_pool[t + (exp ? ".export.loptions" : ".loptions")])); + + append_options (args, *g, var); + } + */ + }; - if (la) - { - os << "Libs.private:"; + // Pretend we are linking an executable using what would be normal, + // system-default link order. + // + linfo li {otype::e, la ? lorder::a_s : lorder::s_a}; - priv = true; process_libraries (a, bs, li, sys_lib_dirs, l, la, 0, // Link flags. - imp, lib, opt, false); + imp, lib, opt, true); os << endl; + + if (la) + { + os << "Libs.private:"; + + priv = true; + process_libraries (a, bs, li, sys_lib_dirs, + l, la, 0, // Link flags. + imp, lib, opt, false); + os << endl; + } } // If we have modules, list them in the modules variable. We also save diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx index 268bab2..507395c 100644 --- a/build2/cc/windows-rpath.cxx +++ b/build2/cc/windows-rpath.cxx @@ -77,7 +77,7 @@ namespace build2 { // This can be an "undiscovered" DLL (see search_library()). // - if (!l->is_a<libs> () || l->path ().empty ()) + if (!l->is_a<libs> () || l->path ().empty ()) // Covers binless. return; } else @@ -152,7 +152,7 @@ namespace build2 if (l != nullptr) { - if (l->is_a<libs> () && !l->path ().empty ()) + if (l->is_a<libs> () && !l->path ().empty ()) // Covers binless. { // Get .pdb if there is one. // diff --git a/build2/install/rule.cxx b/build2/install/rule.cxx index 67f0e86..020551c 100644 --- a/build2/install/rule.cxx +++ b/build2/install/rule.cxx @@ -847,7 +847,11 @@ namespace build2 perform_install (action a, const target& xt) const { const file& t (xt.as<file> ()); - assert (!t.path ().empty ()); // Should have been assigned by update. + const path& tp (t.path ()); + + // Path should have been assigned by update unless it is unreal. + // + assert (!tp.empty () || t.mtime () == timestamp_unreal); const scope& rs (t.root_scope ()); @@ -916,15 +920,22 @@ namespace build2 for (const target* m (t.member); m != nullptr; m = m->member) { if (const path* p = lookup_install<path> (*m, "install")) - install_target (m->as<file> (), *p, false); + { + install_target (m->as<file> (), *p, tp.empty () /* verbose */); + r |= target_state::changed; + } } // Finally install the target itself (since we got here we know the // install variable is there). // - install_target (t, cast<path> (t["install"]), true); + if (!tp.empty ()) + { + install_target (t, cast<path> (t["install"]), true /* verbose */); + r |= target_state::changed; + } - return (r |= target_state::changed); + return r; } // uninstall -d <dir> @@ -1103,7 +1114,11 @@ namespace build2 perform_uninstall (action a, const target& xt) const { const file& t (xt.as<file> ()); - assert (!t.path ().empty ()); // Should have been assigned by update. + const path& tp (t.path ()); + + // Path should have been assigned by update unless it is unreal. + // + assert (!tp.empty () || t.mtime () == timestamp_unreal); const scope& rs (t.root_scope ()); @@ -1154,7 +1169,10 @@ namespace build2 // Reverse order of installation: first the target itself (since we got // here we know the install variable is there). // - target_state r (uninstall_target (t, cast<path> (t["install"]), true)); + target_state r (target_state::unchanged); + + if (!tp.empty ()) + r |= uninstall_target (t, cast<path> (t["install"]), true); // Then installable ad hoc group members, if any. To be anally precise // we would have to do it in reverse, but that's not easy (it's a @@ -1163,8 +1181,9 @@ namespace build2 for (const target* m (t.member); m != nullptr; m = m->member) { if (const path* p = lookup_install<path> (*m, "install")) - r |= uninstall_target ( - m->as<file> (), *p, r != target_state::changed); + r |= uninstall_target (m->as<file> (), + *p, + tp.empty () || r != target_state::changed); } // Finally handle installable prerequisites. |