2222from llnl .util import tty
2323from llnl .util .compat import Sequence
2424from llnl .util .lang import dedupe , memoized
25- from llnl .util .symlink import symlink
25+ from llnl .util .symlink import islink , symlink
2626
2727from spack .util .executable import Executable
2828from spack .util .path import path_to_os_path , system_path_filter
@@ -637,7 +637,11 @@ def copy_tree(src, dest, symlinks=True, ignore=None, _permissions=False):
637637 if symlinks :
638638 target = os .readlink (s )
639639 if os .path .isabs (target ):
640- new_target = re .sub (abs_src , abs_dest , target )
640+
641+ def escaped_path (path ):
642+ return path .replace ("\\ " , r"\\" )
643+
644+ new_target = re .sub (escaped_path (abs_src ), escaped_path (abs_dest ), target )
641645 if new_target != target :
642646 tty .debug ("Redirecting link {0} to {1}" .format (target , new_target ))
643647 target = new_target
@@ -1903,7 +1907,11 @@ def names(self):
19031907 name = x [3 :]
19041908
19051909 # Valid extensions include: ['.dylib', '.so', '.a']
1906- for ext in [".dylib" , ".so" , ".a" ]:
1910+ # on non Windows platform
1911+ # Windows valid library extensions are:
1912+ # ['.dll', '.lib']
1913+ valid_exts = [".dll" , ".lib" ] if is_windows else [".dylib" , ".so" , ".a" ]
1914+ for ext in valid_exts :
19071915 i = name .rfind (ext )
19081916 if i != - 1 :
19091917 names .append (name [:i ])
@@ -2046,15 +2054,23 @@ def find_libraries(libraries, root, shared=True, recursive=False):
20462054 message = message .format (find_libraries .__name__ , type (libraries ))
20472055 raise TypeError (message )
20482056
2057+ if is_windows :
2058+ static = "lib"
2059+ shared = "dll"
2060+ else :
2061+ # Used on both Linux and macOS
2062+ static = "a"
2063+ shared = "so"
2064+
20492065 # Construct the right suffix for the library
20502066 if shared :
20512067 # Used on both Linux and macOS
2052- suffixes = ["so" ]
2068+ suffixes = [shared ]
20532069 if sys .platform == "darwin" :
20542070 # Only used on macOS
20552071 suffixes .append ("dylib" )
20562072 else :
2057- suffixes = ["a" ]
2073+ suffixes = [static ]
20582074
20592075 # List of libraries we are searching with suffixes
20602076 libraries = ["{0}.{1}" .format (lib , suffix ) for lib in libraries for suffix in suffixes ]
@@ -2067,7 +2083,11 @@ def find_libraries(libraries, root, shared=True, recursive=False):
20672083 # perform first non-recursive search in root/lib then in root/lib64 and
20682084 # finally search all of root recursively. The search stops when the first
20692085 # match is found.
2070- for subdir in ("lib" , "lib64" ):
2086+ common_lib_dirs = ["lib" , "lib64" ]
2087+ if is_windows :
2088+ common_lib_dirs .extend (["bin" , "Lib" ])
2089+
2090+ for subdir in common_lib_dirs :
20712091 dirname = join_path (root , subdir )
20722092 if not os .path .isdir (dirname ):
20732093 continue
@@ -2080,6 +2100,155 @@ def find_libraries(libraries, root, shared=True, recursive=False):
20802100 return LibraryList (found_libs )
20812101
20822102
2103+ def find_all_shared_libraries (root , recursive = False ):
2104+ """Convenience function that returns the list of all shared libraries found
2105+ in the directory passed as argument.
2106+
2107+ See documentation for `llnl.util.filesystem.find_libraries` for more information
2108+ """
2109+ return find_libraries ("*" , root = root , shared = True , recursive = recursive )
2110+
2111+
2112+ def find_all_static_libraries (root , recursive = False ):
2113+ """Convenience function that returns the list of all static libraries found
2114+ in the directory passed as argument.
2115+
2116+ See documentation for `llnl.util.filesystem.find_libraries` for more information
2117+ """
2118+ return find_libraries ("*" , root = root , shared = False , recursive = recursive )
2119+
2120+
2121+ def find_all_libraries (root , recursive = False ):
2122+ """Convenience function that returns the list of all libraries found
2123+ in the directory passed as argument.
2124+
2125+ See documentation for `llnl.util.filesystem.find_libraries` for more information
2126+ """
2127+
2128+ return find_all_shared_libraries (root , recursive = recursive ) + find_all_static_libraries (
2129+ root , recursive = recursive
2130+ )
2131+
2132+
2133+ class WindowsSimulatedRPath (object ):
2134+ """Class representing Windows filesystem rpath analog
2135+
2136+ One instance of this class is associated with a package (only on Windows)
2137+ For each lib/binary directory in an associated package, this class introduces
2138+ a symlink to any/all dependent libraries/binaries. This includes the packages
2139+ own bin/lib directories, meaning the libraries are linked to the bianry directory
2140+ and vis versa.
2141+ """
2142+
2143+ def __init__ (self , package , link_install_prefix = True ):
2144+ """
2145+ Args:
2146+ package (spack.package_base.PackageBase): Package requiring links
2147+ link_install_prefix (bool): Link against package's own install or stage root.
2148+ Packages that run their own executables during build and require rpaths to
2149+ the build directory during build time require this option. Default: install
2150+ root
2151+ """
2152+ self .pkg = package
2153+ self ._addl_rpaths = set ()
2154+ self .link_install_prefix = link_install_prefix
2155+ self ._internal_links = set ()
2156+
2157+ @property
2158+ def link_dest (self ):
2159+ """
2160+ Set of directories where package binaries/libraries are located.
2161+ """
2162+ if hasattr (self .pkg , "libs" ) and self .pkg .libs :
2163+ pkg_libs = set (self .pkg .libs .directories )
2164+ else :
2165+ pkg_libs = set ((self .pkg .prefix .lib , self .pkg .prefix .lib64 ))
2166+
2167+ return pkg_libs | set ([self .pkg .prefix .bin ]) | self .internal_links
2168+
2169+ @property
2170+ def internal_links (self ):
2171+ """
2172+ linking that would need to be established within the package itself. Useful for links
2173+ against extension modules/build time executables/internal linkage
2174+ """
2175+ return self ._internal_links
2176+
2177+ def add_internal_links (self , * dest ):
2178+ """
2179+ Incorporate additional paths into the rpath (sym)linking scheme.
2180+
2181+ Paths provided to this method are linked against by a package's libraries
2182+ and libraries found at these paths are linked against a package's binaries.
2183+ (i.e. /site-packages -> /bin and /bin -> /site-packages)
2184+
2185+ Specified paths should be outside of a package's lib, lib64, and bin
2186+ directories.
2187+ """
2188+ self ._internal_links = self ._internal_links | set (* dest )
2189+
2190+ @property
2191+ def link_targets (self ):
2192+ """
2193+ Set of libraries this package needs to link against during runtime
2194+ These packages will each be symlinked into the packages lib and binary dir
2195+ """
2196+
2197+ dependent_libs = []
2198+ for path in self .pkg .rpath :
2199+ dependent_libs .extend (list (find_all_shared_libraries (path , recursive = True )))
2200+ for extra_path in self ._addl_rpaths :
2201+ dependent_libs .extend (list (find_all_shared_libraries (extra_path , recursive = True )))
2202+ return set (dependent_libs )
2203+
2204+ def include_additional_link_paths (self , * paths ):
2205+ """
2206+ Add libraries found at the root of provided paths to runtime linking
2207+
2208+ These are libraries found outside of the typical scope of rpath linking
2209+ that require manual inclusion in a runtime linking scheme
2210+
2211+ Args:
2212+ *paths (str): arbitrary number of paths to be added to runtime linking
2213+ """
2214+ self ._addl_rpaths = self ._addl_rpaths | set (paths )
2215+
2216+ def establish_link (self ):
2217+ """
2218+ (sym)link packages to runtime dependencies based on RPath configuration for
2219+ Windows heuristics
2220+ """
2221+ # from build_environment.py:463
2222+ # The top-level package is always RPATHed. It hasn't been installed yet
2223+ # so the RPATHs are added unconditionally
2224+
2225+ # for each binary install dir in self.pkg (i.e. pkg.prefix.bin, pkg.prefix.lib)
2226+ # install a symlink to each dependent library
2227+ for library , lib_dir in itertools .product (self .link_targets , self .link_dest ):
2228+ if not path_contains_subdirectory (library , lib_dir ):
2229+ file_name = os .path .basename (library )
2230+ dest_file = os .path .join (lib_dir , file_name )
2231+ if os .path .exists (lib_dir ):
2232+ try :
2233+ symlink (library , dest_file )
2234+ # For py2 compatibility, we have to catch the specific Windows error code
2235+ # associate with trying to create a file that already exists (winerror 183)
2236+ except OSError as e :
2237+ if e .winerror == 183 :
2238+ # We have either already symlinked or we are encoutering a naming clash
2239+ # either way, we don't want to overwrite existing libraries
2240+ already_linked = islink (dest_file )
2241+ tty .debug (
2242+ "Linking library %s to %s failed, " % (library , dest_file )
2243+ + "already linked."
2244+ if already_linked
2245+ else "library with name %s already exists." % file_name
2246+ )
2247+ pass
2248+ else :
2249+ raise e
2250+
2251+
20832252@system_path_filter
20842253@memoized
20852254def can_access_dir (path ):
0 commit comments