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/windows-rpath.cxx | 400 -------------------------------------------- 1 file changed, 400 deletions(-) delete mode 100644 build2/cc/windows-rpath.cxx (limited to 'build2/cc/windows-rpath.cxx') diff --git a/build2/cc/windows-rpath.cxx b/build2/cc/windows-rpath.cxx deleted file mode 100644 index c4ef358..0000000 --- a/build2/cc/windows-rpath.cxx +++ /dev/null @@ -1,400 +0,0 @@ -// file : build2/cc/windows-rpath.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include // E* - -#include -#include -#include -#include -#include -#include - -#include - -#include - -using namespace std; -using namespace butl; - -namespace build2 -{ - namespace cc - { - // Provide limited emulation of the rpath functionality on Windows using a - // side-by-side assembly. In a nutshell, the idea is to create an assembly - // with links to all the prerequisite DLLs. - // - // Note that currently our assemblies contain all the DLLs that the - // executable depends on, recursively. The alternative approach could be - // to also create assemblies for DLLs. This appears to be possible (but we - // will have to use the resource ID 2 for such a manifest). And it will - // probably be necessary for DLLs that are loaded dynamically with - // LoadLibrary(). The tricky part is how such nested assemblies will be - // found. Since we are effectively (from the loader's point of view) - // copying the DLLs, we will also have to copy their assemblies (because - // the loader looks for them in the same directory as the DLL). It's not - // clear how well such nested assemblies are supported (e.g., in Wine). - // - // 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. - // - using namespace bin; - - // Return the greatest (newest) timestamp of all the DLLs that we will be - // adding to the assembly or timestamp_nonexistent if there aren't any. - // - timestamp link_rule:: - windows_rpath_timestamp (const file& t, - const scope& bs, - action a, - linfo li) const - { - timestamp r (timestamp_nonexistent); - - // We need to collect all the DLLs, so go into implementation of both - // shared and static (in case they depend on shared). - // - auto imp = [] (const file&, bool) {return true;}; - - auto lib = [&r] (const file* const* lc, - const string& f, - lflags, - bool sys) - { - const file* l (lc != nullptr ? *lc : nullptr); - - // We don't rpath system libraries. - // - if (sys) - return; - - // Skip static libraries. - // - if (l != nullptr) - { - // This can be an "undiscovered" DLL (see search_library()). - // - if (!l->is_a () || l->path ().empty ()) // Also covers binless. - return; - } - else - { - // This is an absolute path and we need to decide whether it is - // a shared or static library. - // - // @@ This is so broken: we don't link to DLLs, we link to .lib or - // .dll.a! Should we even bother? Maybe only for "our" DLLs - // (.dll.lib/.dll.a)? But the DLL can also be in a different - // directory (lib/../bin). - // - // Though this can happen on MinGW with direct DLL link... - // - size_t p (path::traits_type::find_extension (f)); - - if (p == string::npos || casecmp (f.c_str () + p + 1, "dll") != 0) - return; - } - - // Ok, this is a DLL. - // - timestamp t (l != nullptr - ? l->load_mtime () - : mtime (f.c_str ())); - - if (t > r) - r = t; - }; - - for (const prerequisite_target& pt: t.prerequisite_targets[a]) - { - if (pt == nullptr || pt.adhoc) - continue; - - bool la; - const file* f; - - if ((la = (f = pt->is_a ())) || - (la = (f = pt->is_a ())) || // See through. - ( f = pt->is_a ())) - process_libraries (a, bs, li, sys_lib_dirs, - *f, la, pt.data, - imp, lib, nullptr, true); - } - - return r; - } - - // Like *_timestamp() but actually collect the DLLs (and weed out the - // duplicates). - // - auto link_rule:: - windows_rpath_dlls (const file& t, - const scope& bs, - action a, - linfo li) const -> windows_dlls - { - windows_dlls r; - - auto imp = [] (const file&, bool) {return true;}; - - auto lib = [&r, &bs] (const file* const* lc, - const string& f, - lflags, - bool sys) - { - const file* l (lc != nullptr ? *lc : nullptr); - - if (sys) - return; - - if (l != nullptr) - { - if (l->is_a () && !l->path ().empty ()) // Also covers binless. - { - // Get .pdb if there is one. - // - const target_type* tt (bs.find_target_type ("pdb")); - const target* pdb (tt != nullptr - ? find_adhoc_member (*l, *tt) - : nullptr); - r.insert ( - windows_dll { - f, - pdb != nullptr ? &pdb->as ().path ().string () : nullptr, - string () - }); - } - } - else - { - size_t p (path::traits_type::find_extension (f)); - - if (p != string::npos && casecmp (f.c_str () + p + 1, "dll") == 0) - { - // See if we can find a corresponding .pdb. - // - windows_dll wd {f, nullptr, string ()}; - string& pdb (wd.pdb_storage); - - // First try "our" naming: foo.dll.pdb. - // - pdb = f; - pdb += ".pdb"; - - if (!exists (path (pdb))) - { - // Then try the usual naming: foo.pdb. - // - pdb.assign (f, 0, p); - pdb += ".pdb"; - - if (!exists (path (pdb))) - pdb.clear (); - } - - if (!pdb.empty ()) - wd.pdb = &pdb; - - r.insert (move (wd)); - } - } - }; - - for (const prerequisite_target& pt: t.prerequisite_targets[a]) - { - if (pt == nullptr || pt.adhoc) - continue; - - bool la; - const file* f; - - if ((la = (f = pt->is_a ())) || - (la = (f = pt->is_a ())) || // See through. - ( f = pt->is_a ())) - process_libraries (a, bs, li, sys_lib_dirs, - *f, la, pt.data, - imp, lib, nullptr, true); - } - - return r; - } - - const char* - windows_manifest_arch (const string& tcpu); // windows-manifest.cxx - - // The ts argument should be the DLLs timestamp returned by *_timestamp(). - // - // The scratch argument should be true if the DLL set has changed and we - // need to regenerate everything from scratch. Otherwise, we try to avoid - // unnecessary work by comparing the DLLs timestamp against the assembly - // manifest file. - // - void link_rule:: - windows_rpath_assembly (const file& t, - const scope& bs, - action a, - linfo li, - const string& tcpu, - timestamp ts, - bool scratch) const - { - // Assembly paths and name. - // - dir_path ad (path_cast (t.path () + ".dlls")); - string an (ad.leaf ().string ()); - path am (ad / path (an + ".manifest")); - - // First check if we actually need to do anything. Since most of the - // time we won't, we don't want to combine it with the *_dlls() call - // below which allocates memory, etc. - // - if (!scratch) - { - // The corner case here is when _timestamp() returns nonexistent - // signalling that there aren't any DLLs but the assembly manifest - // file exists. This, however, can only happen if we somehow managed - // to transition from the "have DLLs" state to "no DLLs" without going - // through the "from scratch" update. Actually this can happen when - // switching to update-for-install. - // - if (ts != timestamp_nonexistent && ts <= mtime (am)) - return; - } - - // Next collect the set of DLLs that will be in our assembly. We need to - // do this recursively which means we may end up with duplicates. Also, - // it is possible that there aren't/no longer are any DLLs which means - // we just need to clean things up. - // - bool empty (ts == timestamp_nonexistent); - - windows_dlls dlls; - if (!empty) - dlls = windows_rpath_dlls (t, bs, a, li); - - // Clean the assembly directory and make sure it exists. Maybe it would - // have been faster to overwrite the existing manifest rather than - // removing the old one and creating a new one. But this is definitely - // simpler. - // - { - rmdir_status s (rmdir_r (t.ctx, ad, empty, 3)); - - if (empty) - return; - - if (s == rmdir_status::not_exist) - mkdir (ad, 3); - } - - // Symlink or copy the DLLs. - // - { - const scope& as (t.weak_scope ()); // Amalgamation. - - auto link = [&as] (const path& f, const path& l) - { - auto print = [&f, &l] (const char* cmd) - { - if (verb >= 3) - text << cmd << ' ' << f << ' ' << l; - }; - - // First we try to create a symlink. If that fails (e.g., "Windows - // happens"), then we resort to hard links. If that doesn't work - // out either (e.g., not on the same filesystem), then we fall back - // to copies. - // - // For the symlink use a relative target path if both paths are part - // of the same amalgamation. This way if the amalgamation is moved - // as a whole, the links will remain valid. - // - try - { - switch (mkanylink (f, l, - true /* copy */, - f.sub (as.out_path ()) /* relative */)) - { - case entry_type::regular: print ("cp"); break; - case entry_type::symlink: print ("ln -s"); break; - case entry_type::other: print ("ln"); break; - default: assert (false); - } - } - catch (const pair& e) - { - const char* w (nullptr); - switch (e.first) - { - case entry_type::regular: print ("cp"); w = "copy"; break; - case entry_type::symlink: print ("ln -s"); w = "symlink"; break; - case entry_type::other: print ("ln"); w = "hardlink"; break; - default: assert (false); - } - - fail << "unable to make " << w << ' ' << l << ": " << e.second; - } - }; - - for (const windows_dll& wd: dlls) - { - //@@ Would be nice to avoid copying. Perhaps reuse buffers - // by adding path::assign() and traits::leaf(). - // - path dp (wd.dll); // DLL path. - path dn (dp.leaf ()); // DLL name. - - link (dp, ad / dn); - - // Link .pdb if there is one. - // - if (wd.pdb != nullptr) - { - path pp (*wd.pdb); - link (pp, ad / pp.leaf ()); - } - } - } - - if (verb >= 3) - text << "cat >" << am; - - if (t.ctx.dry_run) - return; - - auto_rmfile rm (am); - - try - { - ofdstream os (am); - - const char* pa (windows_manifest_arch (tcpu)); - - os << "\n" - << "\n" - << " \n"; - - - - for (const windows_dll& wd: dlls) - os << " \n"; - - os << "\n"; - - os.close (); - rm.cancel (); - } - catch (const io_error& e) - { - fail << "unable to write to " << am << ": " << e; - } - } - } -} -- cgit v1.1