aboutsummaryrefslogtreecommitdiff
path: root/build2/cc/windows-rpath.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build2/cc/windows-rpath.cxx')
-rw-r--r--build2/cc/windows-rpath.cxx400
1 files changed, 0 insertions, 400 deletions
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 <errno.h> // E*
-
-#include <libbuild2/scope.hxx>
-#include <libbuild2/context.hxx>
-#include <libbuild2/variable.hxx>
-#include <libbuild2/algorithm.hxx>
-#include <libbuild2/filesystem.hxx>
-#include <libbuild2/diagnostics.hxx>
-
-#include <libbuild2/bin/target.hxx>
-
-#include <build2/cc/link-rule.hxx>
-
-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<libs> () || 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<liba> ())) ||
- (la = (f = pt->is_a<libux> ())) || // See through.
- ( f = pt->is_a<libs> ()))
- 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<libs> () && !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<file> ().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<liba> ())) ||
- (la = (f = pt->is_a<libux> ())) || // See through.
- ( f = pt->is_a<libs> ()))
- 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<dir_path> (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<entry_type, system_error>& 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 << "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
- << "<assembly xmlns='urn:schemas-microsoft-com:asm.v1'\n"
- << " manifestVersion='1.0'>\n"
- << " <assemblyIdentity name='" << an << "'\n"
- << " type='win32'\n"
- << " processorArchitecture='" << pa << "'\n"
- << " version='0.0.0.0'/>\n";
-
-
-
- for (const windows_dll& wd: dlls)
- os << " <file name='" << path (wd.dll).leaf () << "'/>\n";
-
- os << "</assembly>\n";
-
- os.close ();
- rm.cancel ();
- }
- catch (const io_error& e)
- {
- fail << "unable to write to " << am << ": " << e;
- }
- }
- }
-}