summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/external/altgraph/__init__.py9
-rw-r--r--lib/spack/spack/binary_distribution.py256
-rw-r--r--lib/spack/spack/relocate.py697
-rw-r--r--lib/spack/spack/test/packaging.py519
4 files changed, 896 insertions, 585 deletions
diff --git a/lib/spack/external/altgraph/__init__.py b/lib/spack/external/altgraph/__init__.py
index 289c6408d1..ee70a9c91b 100644
--- a/lib/spack/external/altgraph/__init__.py
+++ b/lib/spack/external/altgraph/__init__.py
@@ -139,9 +139,12 @@ To display the graph we can use the GraphViz backend::
@contributor: U{Reka Albert <http://www.phys.psu.edu/~ralbert/>}
'''
-import pkg_resources
-__version__ = pkg_resources.require('altgraph')[0].version
-
+# import pkg_resources
+# __version__ = pkg_resources.require('altgraph')[0].version
+# pkg_resources is not finding the altgraph import despite the fact that it is in sys.path
+# there is no .dist-info or .egg-info for pkg_resources to query the version from
+# so it must be set manually
+__version__ = '0.16.1'
class GraphError(ValueError):
pass
diff --git a/lib/spack/spack/binary_distribution.py b/lib/spack/spack/binary_distribution.py
index 0569bcc665..7902e5fc58 100644
--- a/lib/spack/spack/binary_distribution.py
+++ b/lib/spack/spack/binary_distribution.py
@@ -10,6 +10,9 @@ import tarfile
import shutil
import tempfile
import hashlib
+import glob
+import platform
+
from contextlib import closing
import ruamel.yaml as yaml
@@ -53,7 +56,7 @@ BUILD_CACHE_INDEX_TEMPLATE = '''
BUILD_CACHE_INDEX_ENTRY_TEMPLATE = ' <li><a href="{path}">{path}</a></li>'
-class NoOverwriteException(Exception):
+class NoOverwriteException(spack.error.SpackError):
"""
Raised when a file exists and must be overwritten.
"""
@@ -68,14 +71,18 @@ class NoGpgException(spack.error.SpackError):
"""
Raised when gpg2 is not in PATH
"""
- pass
+
+ def __init__(self, msg):
+ super(NoGpgException, self).__init__(msg)
class NoKeyException(spack.error.SpackError):
"""
Raised when gpg has no default key added.
"""
- pass
+
+ def __init__(self, msg):
+ super(NoKeyException, self).__init__(msg)
class PickKeyException(spack.error.SpackError):
@@ -84,7 +91,7 @@ class PickKeyException(spack.error.SpackError):
"""
def __init__(self, keys):
- err_msg = "Multi keys available for signing\n%s\n" % keys
+ err_msg = "Multiple keys available for signing\n%s\n" % keys
err_msg += "Use spack buildcache create -k <key hash> to pick a key."
super(PickKeyException, self).__init__(err_msg)
@@ -107,7 +114,9 @@ class NewLayoutException(spack.error.SpackError):
"""
Raised if directory layout is different from buildcache.
"""
- pass
+
+ def __init__(self, msg):
+ super(NewLayoutException, self).__init__(msg)
def build_cache_relative_path():
@@ -137,15 +146,21 @@ def read_buildinfo_file(prefix):
return buildinfo
-def write_buildinfo_file(prefix, workdir, rel=False):
+def write_buildinfo_file(spec, workdir, rel=False):
"""
Create a cache file containing information
required for the relocation
"""
+ prefix = spec.prefix
text_to_relocate = []
binary_to_relocate = []
link_to_relocate = []
blacklist = (".spack", "man")
+ prefix_to_hash = dict()
+ prefix_to_hash[str(spec.package.prefix)] = spec.dag_hash()
+ deps = spack.build_environment.get_rpath_deps(spec.package)
+ for d in deps:
+ prefix_to_hash[str(d.prefix)] = d.dag_hash()
# Do this at during tarball creation to save time when tarball unpacked.
# Used by make_package_relative to determine binaries to change.
for root, dirs, files in os.walk(prefix, topdown=True):
@@ -162,8 +177,8 @@ def write_buildinfo_file(prefix, workdir, rel=False):
link_to_relocate.append(rel_path_name)
else:
msg = 'Absolute link %s to %s ' % (path_name, link)
- msg += 'outside of stage %s ' % prefix
- msg += 'cannot be relocated.'
+ msg += 'outside of prefix %s ' % prefix
+ msg += 'should not be relocated.'
tty.warn(msg)
if relocate.needs_binary_relocation(m_type, m_subtype):
@@ -184,6 +199,7 @@ def write_buildinfo_file(prefix, workdir, rel=False):
buildinfo['relocate_textfiles'] = text_to_relocate
buildinfo['relocate_binaries'] = binary_to_relocate
buildinfo['relocate_links'] = link_to_relocate
+ buildinfo['prefix_to_hash'] = prefix_to_hash
filename = buildinfo_file_name(workdir)
with open(filename, 'w') as outfile:
outfile.write(syaml.dump(buildinfo, default_flow_style=True))
@@ -356,7 +372,7 @@ def build_tarball(spec, outdir, force=False, rel=False, unsigned=False,
os.remove(temp_tarfile_path)
# create info for later relocation and create tar
- write_buildinfo_file(spec.prefix, workdir, rel=rel)
+ write_buildinfo_file(spec, workdir, rel)
# optionally make the paths in the binaries relative to each other
# in the spack install tree before creating tarball
@@ -370,7 +386,7 @@ def build_tarball(spec, outdir, force=False, rel=False, unsigned=False,
tty.die(e)
else:
try:
- make_package_placeholder(workdir, spec, allow_root)
+ check_package_relocatable(workdir, spec, allow_root)
except Exception as e:
shutil.rmtree(workdir)
shutil.rmtree(tarfile_dir)
@@ -400,6 +416,7 @@ def build_tarball(spec, outdir, force=False, rel=False, unsigned=False,
buildinfo = {}
buildinfo['relative_prefix'] = os.path.relpath(
spec.prefix, spack.store.layout.root)
+ buildinfo['relative_rpaths'] = rel
spec_dict['buildinfo'] = buildinfo
spec_dict['full_hash'] = spec.full_hash()
@@ -481,100 +498,149 @@ def make_package_relative(workdir, spec, allow_root):
"""
prefix = spec.prefix
buildinfo = read_buildinfo_file(workdir)
- old_path = buildinfo['buildpath']
+ old_layout_root = buildinfo['buildpath']
orig_path_names = list()
cur_path_names = list()
for filename in buildinfo['relocate_binaries']:
orig_path_names.append(os.path.join(prefix, filename))
cur_path_names.append(os.path.join(workdir, filename))
- if spec.architecture.platform == 'darwin':
+ if (spec.architecture.platform == 'darwin' or
+ spec.architecture.platform == 'test' and
+ platform.system().lower() == 'darwin'):
relocate.make_macho_binaries_relative(cur_path_names, orig_path_names,
- old_path, allow_root)
- else:
+ old_layout_root)
+ if (spec.architecture.platform == 'linux' or
+ spec.architecture.platform == 'test' and
+ platform.system().lower() == 'linux'):
relocate.make_elf_binaries_relative(cur_path_names, orig_path_names,
- old_path, allow_root)
+ old_layout_root)
+ relocate.check_files_relocatable(cur_path_names, allow_root)
orig_path_names = list()
cur_path_names = list()
- for filename in buildinfo.get('relocate_links', []):
- orig_path_names.append(os.path.join(prefix, filename))
- cur_path_names.append(os.path.join(workdir, filename))
+ for linkname in buildinfo.get('relocate_links', []):
+ orig_path_names.append(os.path.join(prefix, linkname))
+ cur_path_names.append(os.path.join(workdir, linkname))
relocate.make_link_relative(cur_path_names, orig_path_names)
-def make_package_placeholder(workdir, spec, allow_root):
+def check_package_relocatable(workdir, spec, allow_root):
"""
Check if package binaries are relocatable.
Change links to placeholder links.
"""
- prefix = spec.prefix
buildinfo = read_buildinfo_file(workdir)
cur_path_names = list()
for filename in buildinfo['relocate_binaries']:
cur_path_names.append(os.path.join(workdir, filename))
relocate.check_files_relocatable(cur_path_names, allow_root)
- cur_path_names = list()
- for filename in buildinfo.get('relocate_links', []):
- cur_path_names.append(os.path.join(workdir, filename))
- relocate.make_link_placeholder(cur_path_names, workdir, prefix)
-
-def relocate_package(workdir, spec, allow_root):
+def relocate_package(spec, allow_root):
"""
Relocate the given package
"""
+ workdir = str(spec.prefix)
buildinfo = read_buildinfo_file(workdir)
- new_path = str(spack.store.layout.root)
- new_prefix = str(spack.paths.prefix)
- old_path = str(buildinfo['buildpath'])
- old_prefix = str(buildinfo.get('spackprefix',
- '/not/in/buildinfo/dictionary'))
- rel = buildinfo.get('relative_rpaths', False)
-
- tty.msg("Relocating package from",
- "%s to %s." % (old_path, new_path))
- path_names = set()
+ new_layout_root = str(spack.store.layout.root)
+ new_prefix = str(spec.prefix)
+ new_rel_prefix = str(os.path.relpath(new_prefix, new_layout_root))
+ new_spack_prefix = str(spack.paths.prefix)
+ old_layout_root = str(buildinfo['buildpath'])
+ old_spack_prefix = str(buildinfo.get('spackprefix'))
+ old_rel_prefix = buildinfo.get('relative_prefix')
+ old_prefix = os.path.join(old_layout_root, old_rel_prefix)
+ rel = buildinfo.get('relative_rpaths')
+ prefix_to_hash = buildinfo.get('prefix_to_hash', None)
+ if (old_rel_prefix != new_rel_prefix and not prefix_to_hash):
+ msg = "Package tarball was created from an install "
+ msg += "prefix with a different directory layout and an older "
+ msg += "buildcache create implementation. It cannot be relocated."
+ raise NewLayoutException(msg)
+ # older buildcaches do not have the prefix_to_hash dictionary
+ # need to set an empty dictionary and add one entry to
+ # prefix_to_prefix to reproduce the old behavior
+ if not prefix_to_hash:
+ prefix_to_hash = dict()
+ hash_to_prefix = dict()
+ hash_to_prefix[spec.format('{hash}')] = str(spec.package.prefix)
+ new_deps = spack.build_environment.get_rpath_deps(spec.package)
+ for d in new_deps:
+ hash_to_prefix[d.format('{hash}')] = str(d.prefix)
+ prefix_to_prefix = dict()
+ for orig_prefix, hash in prefix_to_hash.items():
+ prefix_to_prefix[orig_prefix] = hash_to_prefix.get(hash, None)
+ prefix_to_prefix[old_prefix] = new_prefix
+ prefix_to_prefix[old_layout_root] = new_layout_root
+
+ tty.debug("Relocating package from",
+ "%s to %s." % (old_layout_root, new_layout_root))
+
+ def is_backup_file(file):
+ return file.endswith('~')
+
+ # Text files containing the prefix text
+ text_names = list()
for filename in buildinfo['relocate_textfiles']:
- path_name = os.path.join(workdir, filename)
+ text_name = os.path.join(workdir, filename)
# Don't add backup files generated by filter_file during install step.
- if not path_name.endswith('~'):
- path_names.add(path_name)
- relocate.relocate_text(path_names, oldpath=old_path,
- newpath=new_path, oldprefix=old_prefix,
- newprefix=new_prefix)
- # If the binary files in the package were not edited to use
- # relative RPATHs, then the RPATHs need to be relocated
- if rel:
- if old_path != new_path:
- files_to_relocate = list(filter(
- lambda pathname: not relocate.file_is_relocatable(
- pathname, paths_to_relocate=[old_path, old_prefix]),
- map(lambda filename: os.path.join(workdir, filename),
- buildinfo['relocate_binaries'])))
-
- if len(old_path) < len(new_path) and files_to_relocate:
- tty.debug('Cannot do a binary string replacement with padding '
- 'for package because %s is longer than %s.' %
- (new_path, old_path))
- else:
- for path_name in files_to_relocate:
- relocate.replace_prefix_bin(path_name, old_path, new_path)
- else:
- path_names = set()
- for filename in buildinfo['relocate_binaries']:
- path_name = os.path.join(workdir, filename)
- path_names.add(path_name)
- if spec.architecture.platform == 'darwin':
- relocate.relocate_macho_binaries(path_names, old_path,
- new_path, allow_root)
- else:
- relocate.relocate_elf_binaries(path_names, old_path,
- new_path, allow_root)
- path_names = set()
- for filename in buildinfo.get('relocate_links', []):
- path_name = os.path.join(workdir, filename)
- path_names.add(path_name)
- relocate.relocate_links(path_names, old_path, new_path)
+ if not is_backup_file(text_name):
+ text_names.append(text_name)
+
+# If we are installing back to the same location don't replace anything
+ if old_layout_root != new_layout_root:
+ paths_to_relocate = [old_spack_prefix, old_layout_root]
+ paths_to_relocate.extend(prefix_to_hash.keys())
+ files_to_relocate = list(filter(
+ lambda pathname: not relocate.file_is_relocatable(
+ pathname, paths_to_relocate=paths_to_relocate),
+ map(lambda filename: os.path.join(workdir, filename),
+ buildinfo['relocate_binaries'])))
+ # If the buildcache was not created with relativized rpaths
+ # do the relocation of path in binaries
+ if (spec.architecture.platform == 'darwin' or
+ spec.architecture.platform == 'test' and
+ platform.system().lower() == 'darwin'):
+ relocate.relocate_macho_binaries(files_to_relocate,
+ old_layout_root,
+ new_layout_root,
+ prefix_to_prefix, rel,
+ old_prefix,
+ new_prefix)
+ if (spec.architecture.platform == 'linux' or
+ spec.architecture.platform == 'test' and
+ platform.system().lower() == 'linux'):
+ relocate.relocate_elf_binaries(files_to_relocate,
+ old_layout_root,
+ new_layout_root,
+ prefix_to_prefix, rel,
+ old_prefix,
+ new_prefix)
+ # Relocate links to the new install prefix
+ link_names = [linkname
+ for linkname in buildinfo.get('relocate_links', [])]
+ relocate.relocate_links(link_names,
+ old_layout_root,
+ new_layout_root,
+ old_prefix,
+ new_prefix,
+ prefix_to_prefix)
+
+ # For all buildcaches
+ # relocate the install prefixes in text files including dependencies
+ relocate.relocate_text(text_names,
+ old_layout_root, new_layout_root,
+ old_prefix, new_prefix,
+ old_spack_prefix,
+ new_spack_prefix,
+ prefix_to_prefix)
+
+ # relocate the install prefixes in binary files including dependencies
+ relocate.relocate_text_bin(files_to_relocate,
+ old_layout_root, new_layout_root,
+ old_prefix, new_prefix,
+ old_spack_prefix,
+ new_spack_prefix,
+ prefix_to_prefix)
def extract_tarball(spec, filename, allow_root=False, unsigned=False,
@@ -610,7 +676,7 @@ def extract_tarball(spec, filename, allow_root=False, unsigned=False,
Gpg.verify('%s.asc' % specfile_path, specfile_path, suppress)
except Exception as e:
shutil.rmtree(tmpdir)
- tty.die(e)
+ raise e
else:
shutil.rmtree(tmpdir)
raise NoVerifyException(
@@ -639,22 +705,30 @@ def extract_tarball(spec, filename, allow_root=False, unsigned=False,
# if the original relative prefix is in the spec file use it
buildinfo = spec_dict.get('buildinfo', {})
old_relative_prefix = buildinfo.get('relative_prefix', new_relative_prefix)
+ rel = buildinfo.get('relative_rpaths')
# if the original relative prefix and new relative prefix differ the
# directory layout has changed and the buildcache cannot be installed
- if old_relative_prefix != new_relative_prefix:
- shutil.rmtree(tmpdir)
- msg = "Package tarball was created from an install "
- msg += "prefix with a different directory layout.\n"
- msg += "It cannot be relocated."
- raise NewLayoutException(msg)
+ # if it was created with relative rpaths
+ info = 'old relative prefix %s\nnew relative prefix %s\nrelative rpaths %s'
+ tty.debug(info %
+ (old_relative_prefix, new_relative_prefix, rel))
+# if (old_relative_prefix != new_relative_prefix and (rel)):
+# shutil.rmtree(tmpdir)
+# msg = "Package tarball was created from an install "
+# msg += "prefix with a different directory layout. "
+# msg += "It cannot be relocated because it "
+# msg += "uses relative rpaths."
+# raise NewLayoutException(msg)
# extract the tarball in a temp directory
with closing(tarfile.open(tarfile_path, 'r')) as tar:
tar.extractall(path=tmpdir)
- # the base of the install prefix is used when creating the tarball
- # so the pathname should be the same now that the directory layout
- # is confirmed
- workdir = os.path.join(tmpdir, os.path.basename(spec.prefix))
+ # get the parent directory of the file .spack/binary_distribution
+ # this should the directory unpacked from the tarball whose
+ # name is unknown because the prefix naming is unknown
+ bindist_file = glob.glob('%s/*/.spack/binary_distribution' % tmpdir)[0]
+ workdir = re.sub('/.spack/binary_distribution$', '', bindist_file)
+ tty.debug('workdir %s' % workdir)
# install_tree copies hardlinks
# create a temporary tarfile from prefix and exract it to workdir
# tarfile preserves hardlinks
@@ -672,10 +746,10 @@ def extract_tarball(spec, filename, allow_root=False, unsigned=False,
os.remove(specfile_path)
try:
- relocate_package(spec.prefix, spec, allow_root)
+ relocate_package(spec, allow_root)
except Exception as e:
shutil.rmtree(spec.prefix)
- tty.die(e)
+ raise e
else:
manifest_file = os.path.join(spec.prefix,
spack.store.layout.metadata_dir,
@@ -685,6 +759,8 @@ def extract_tarball(spec, filename, allow_root=False, unsigned=False,
tty.warn('No manifest file in tarball for spec %s' % spec_id)
finally:
shutil.rmtree(tmpdir)
+ if os.path.exists(filename):
+ os.remove(filename)
# Internal cache for downloaded specs
@@ -732,7 +808,7 @@ def get_spec(spec=None, force=False):
tty.debug("No Spack mirrors are currently configured")
return {}
- if spec in _cached_specs:
+ if _cached_specs and spec in _cached_specs:
return _cached_specs
for mirror in spack.mirror.MirrorCollection().values():
@@ -817,7 +893,7 @@ def get_keys(install=False, trust=False, force=False):
mirror_dir = url_util.local_file_path(fetch_url_build_cache)
if mirror_dir:
tty.msg("Finding public keys in %s" % mirror_dir)
- files = os.listdir(mirror_dir)
+ files = os.listdir(str(mirror_dir))
for file in files:
if re.search(r'\.key', file) or re.search(r'\.pub', file):
link = url_util.join(fetch_url_build_cache, file)
diff --git a/lib/spack/spack/relocate.py b/lib/spack/spack/relocate.py
index c8c7947f9c..7a36434736 100644
--- a/lib/spack/spack/relocate.py
+++ b/lib/spack/spack/relocate.py
@@ -13,6 +13,9 @@ import spack.cmd
import llnl.util.lang
from spack.util.executable import Executable, ProcessError
import llnl.util.tty as tty
+from macholib.MachO import MachO
+from spack.spec import Spec
+import macholib.mach_o
class InstallRootStringException(spack.error.SpackError):
@@ -41,45 +44,56 @@ class BinaryStringReplacementException(spack.error.SpackError):
(file_path, old_len, new_len))
-class MissingMacholibException(spack.error.SpackError):
+class BinaryTextReplaceException(spack.error.SpackError):
"""
- Raised when the size of the file changes after binary path substitution.
+ Raised when the new install path is shorter than the old install path
+ so binary text replacement cannot occur.
+ """
+
+ def __init__(self, old_path, new_path):
+ msg = "New path longer than old path: binary text"
+ msg += " replacement not possible."
+ err_msg = "The new path %s" % new_path
+ err_msg += " is longer than the old path %s.\n" % old_path
+ err_msg += "Text replacement in binaries will not work.\n"
+ err_msg += "Create buildcache from an install path "
+ err_msg += "longer than new path."
+ super(BinaryTextReplaceException, self).__init__(msg, err_msg)
+
+
+class PatchelfError(spack.error.SpackError):
+ """
+ Raised when patchelf command returns a ProcessError.
"""
def __init__(self, error):
- super(MissingMacholibException, self).__init__(
- "%s\n"
- "Python package macholib needs to be avaiable to list\n"
- "and modify a mach-o binary's rpaths, deps and id.\n"
- "Use virtualenv with pip install macholib or\n"
- "use spack to install the py-macholib package\n"
- "spack install py-macholib\n"
- "spack activate py-macholib\n"
- "spack load python\n"
- % error)
+ super(PatchelfError, self).__init__(error)
def get_patchelf():
"""
+ Returns the full patchelf binary path if available in $PATH.
Builds and installs spack patchelf package on linux platforms
- using the first concretized spec.
- Returns the full patchelf binary path.
+ using the first concretized spec if it is not installed and
+ returns the full patchelf binary path.
"""
# as we may need patchelf, find out where it is
patchelf = spack.util.executable.which('patchelf')
if patchelf is not None:
return patchelf.path
- else:
- if str(spack.architecture.platform()) == 'test':
- return None
- if str(spack.architecture.platform()) == 'darwin':
- return None
- patchelf_spec = spack.cmd.parse_specs("patchelf", concretize=True)[0]
- patchelf = spack.repo.get(patchelf_spec)
- if not patchelf.installed:
- patchelf.do_install(use_cache=False)
+ patchelf_spec = Spec('patchelf').concretized()
+ patchelf = patchelf_spec.package
+ if patchelf.installed:
patchelf_executable = os.path.join(patchelf.prefix.bin, "patchelf")
return patchelf_executable
+ else:
+ if (str(spack.architecture.platform()) == 'test' or
+ str(spack.architecture.platform()) == 'darwin'):
+ return None
+ else:
+ patchelf.do_install()
+ patchelf_executable = os.path.join(patchelf.prefix.bin, "patchelf")
+ return patchelf_executable
def get_existing_elf_rpaths(path_name):
@@ -95,33 +109,53 @@ def get_existing_elf_rpaths(path_name):
else:
patchelf = Executable(get_patchelf())
+ rpaths = list()
try:
output = patchelf('--print-rpath', '%s' %
path_name, output=str, error=str)
- return output.rstrip('\n').split(':')
+ rpaths = output.rstrip('\n').split(':')
except ProcessError as e:
- tty.debug('patchelf --print-rpath produced an error on %s' %
- path_name, e)
- return []
- return
+ msg = 'patchelf --print-rpath %s produced an error %s' % (path_name, e)
+ raise PatchelfError(msg)
+ return rpaths
-def get_relative_rpaths(path_name, orig_dir, orig_rpaths):
+def get_relative_elf_rpaths(path_name, orig_layout_root, orig_rpaths):
"""
- Replaces orig_dir with relative path from dirname(path_name) if an rpath
- in orig_rpaths contains orig_path. Prefixes $ORIGIN
+ Replaces orig rpath with relative path from dirname(path_name) if an rpath
+ in orig_rpaths contains orig_layout_root. Prefixes $ORIGIN
to relative paths and returns replacement rpaths.
"""
rel_rpaths = []
for rpath in orig_rpaths:
- if re.match(orig_dir, rpath):
+ if re.match(orig_layout_root, rpath):
rel = os.path.relpath(rpath, start=os.path.dirname(path_name))
- rel_rpaths.append('$ORIGIN/%s' % rel)
+ rel_rpaths.append(os.path.join('$ORIGIN', '%s' % rel))
else:
rel_rpaths.append(rpath)
return rel_rpaths
+def get_normalized_elf_rpaths(orig_path_name, rel_rpaths):
+ """
+ Normalize the relative rpaths with respect to the original path name
+ of the file. If the rpath starts with $ORIGIN replace $ORIGIN with the
+ dirname of the original path name and then normalize the rpath.
+ A dictionary mapping relativized rpaths to normalized rpaths is returned.
+ """
+ norm_rpaths = list()
+ for rpath in rel_rpaths:
+ if rpath.startswith('$ORIGIN'):
+ sub = re.sub('$ORIGIN',
+ os.path.dirname(orig_path_name),
+ rpath)
+ norm = os.path.normpath(sub)
+ norm_rpaths.append(norm)
+ else:
+ norm_rpaths.append(rpath)
+ return norm_rpaths
+
+
def set_placeholder(dirname):
"""
return string of @'s with same length
@@ -129,183 +163,157 @@ def set_placeholder(dirname):
return '@' * len(dirname)
-def get_placeholder_rpaths(path_name, orig_rpaths):
+def macho_make_paths_relative(path_name, old_layout_root,
+ rpaths, deps, idpath):
"""
- Replaces original layout root dir with a placeholder string in all rpaths.
+ Return a dictionary mapping the original rpaths to the relativized rpaths.
+ This dictionary is used to replace paths in mach-o binaries.
+ Replace old_dir with relative path from dirname of path name
+ in rpaths and deps; idpath is replaced with @rpath/libname.
"""
- rel_rpaths = []
- orig_dir = spack.store.layout.root
- for rpath in orig_rpaths:
- if re.match(orig_dir, rpath):
- placeholder = set_placeholder(orig_dir)
- rel = re.sub(orig_dir, placeholder, rpath)
- rel_rpaths.append('%s' % rel)
- else:
- rel_rpaths.append(rpath)
- return rel_rpaths
-
-
-def macho_get_paths(path_name):
- """
- Examines the output of otool -l path_name for these three fields:
- LC_ID_DYLIB, LC_LOAD_DYLIB, LC_RPATH and parses out the rpaths,
- dependiencies and library id.
- Returns these values.
- """
- otool = Executable('otool')
- output = otool("-l", path_name, output=str, err=str)
- last_cmd = None
- idpath = None
- rpaths = []
- deps = []
- for line in output.split('\n'):
- match = re.search('( *[a-zA-Z]+ )(.*)', line)
- if match:
- lhs = match.group(1).lstrip().rstrip()
- rhs = match.group(2)
- match2 = re.search(r'(.*) \(.*\)', rhs)
- if match2:
- rhs = match2.group(1)
- if lhs == 'cmd':
- last_cmd = rhs
- if lhs == 'path' and last_cmd == 'LC_RPATH':
- rpaths.append(rhs)
- if lhs == 'name' and last_cmd == 'LC_ID_DYLIB':
- idpath = rhs
- if lhs == 'name' and last_cmd == 'LC_LOAD_DYLIB':
- deps.append(rhs)
- return rpaths, deps, idpath
-
-
-def macho_make_paths_relative(path_name, old_dir, rpaths, deps, idpath):
- """
- Replace old_dir with relative path from dirname(path_name)
- in rpaths and deps; idpaths are replaced with @rpath/libname as needed;
- replacement are returned.
- """
- new_idpath = None
+ paths_to_paths = dict()
if idpath:
- new_idpath = '@rpath/%s' % os.path.basename(idpath)
- new_rpaths = list()
- new_deps = list()
+ paths_to_paths[idpath] = os.path.join(
+ '@rpath', '%s' % os.path.basename(idpath))
for rpath in rpaths:
- if re.match(old_dir, rpath):
+ if re.match(old_layout_root, rpath):
rel = os.path.relpath(rpath, start=os.path.dirname(path_name))
- new_rpaths.append('@loader_path/%s' % rel)
+ paths_to_paths[rpath] = os.path.join('@loader_path', '%s' % rel)
else:
- new_rpaths.append(rpath)
+ paths_to_paths[rpath] = rpath
for dep in deps:
- if re.match(old_dir, dep):
+ if re.match(old_layout_root, dep):
rel = os.path.relpath(dep, start=os.path.dirname(path_name))
- new_deps.append('@loader_path/%s' % rel)
+ paths_to_paths[dep] = os.path.join('@loader_path', '%s' % rel)
else:
- new_deps.append(dep)
- return (new_rpaths, new_deps, new_idpath)
+ paths_to_paths[dep] = dep
+ return paths_to_paths
-def macho_make_paths_placeholder(rpaths, deps, idpath):
+def macho_make_paths_normal(orig_path_name, rpaths, deps, idpath):
"""
- Replace old_dir with a placeholder of the same length
- in rpaths and deps and idpaths is needed.
- replacement are returned.
+ Return a dictionary mapping the relativized rpaths to the original rpaths.
+ This dictionary is used to replace paths in mach-o binaries.
+ Replace '@loader_path' with the dirname of the origname path name
+ in rpaths and deps; idpath is replaced with the original path name
"""
- new_idpath = None
- old_dir = spack.store.layout.root
- placeholder = set_placeholder(old_dir)
+ rel_to_orig = dict()
if idpath:
- new_idpath = re.sub(old_dir, placeholder, idpath)
- new_rpaths = list()
- new_deps = list()
+ rel_to_orig[idpath] = orig_path_name
+
for rpath in rpaths:
- if re.match(old_dir, rpath):
- ph = re.sub(old_dir, placeholder, rpath)
- new_rpaths.append('%s' % ph)
+ if re.match('@loader_path', rpath):
+ norm = os.path.normpath(re.sub(re.escape('@loader_path'),
+ os.path.dirname(orig_path_name),
+ rpath))
+ rel_to_orig[rpath] = norm
else:
- new_rpaths.append(rpath)
+ rel_to_orig[rpath] = rpath
for dep in deps:
- if re.match(old_dir, dep):
- ph = re.sub(old_dir, placeholder, dep)
- new_deps.append('%s' % ph)
+ if re.match('@loader_path', dep):
+ norm = os.path.normpath(re.sub(re.escape('@loader_path'),
+ os.path.dirname(orig_path_name),
+ dep))
+ rel_to_orig[dep] = norm
else:
- new_deps.append(dep)
- return (new_rpaths, new_deps, new_idpath)
-
+ rel_to_orig[dep] = dep
+ return rel_to_orig
+
+
+def macho_find_paths(orig_rpaths, deps, idpath,
+ old_layout_root, prefix_to_prefix):
+ """
+ Inputs
+ original rpaths from mach-o binaries
+ dependency libraries for mach-o binaries
+ id path of mach-o libraries
+ old install directory layout root
+ prefix_to_prefix dictionary which maps prefixes in the old directory layout
+ to directories in the new directory layout
+ Output
+ paths_to_paths dictionary which maps all of the old paths to new paths
+ """
+ paths_to_paths = dict()
+ for orig_rpath in orig_rpaths:
+ if orig_rpath.startswith(old_layout_root):
+ for old_prefix, new_prefix in prefix_to_prefix.items():
+ if orig_rpath.startswith(old_prefix):
+ new_rpath = re.sub(re.escape(old_prefix),
+ new_prefix, orig_rpath)
+ paths_to_paths[orig_rpath] = new_rpath
+ else:
+ paths_to_paths[orig_rpath] = orig_rpath
-def macho_replace_paths(old_dir, new_dir, rpaths, deps, idpath):
- """
- Replace old_dir with new_dir in rpaths, deps and idpath
- and return replacements
- """
- new_idpath = None
if idpath:
- new_idpath = idpath.replace(old_dir, new_dir)
- new_rpaths = list()
- new_deps = list()
- for rpath in rpaths:
- new_rpath = rpath.replace(old_dir, new_dir)
- new_rpaths.append(new_rpath)
+ for old_prefix, new_prefix in prefix_to_prefix.items():
+ if idpath.startswith(old_prefix):
+ paths_to_paths[idpath] = re.sub(
+ re.escape(old_prefix), new_prefix, idpath)
for dep in deps:
- new_dep = dep.replace(old_dir, new_dir)
- new_deps.append(new_dep)
- return new_rpaths, new_deps, new_idpath
+ for old_prefix, new_prefix in prefix_to_prefix.items():
+ if dep.startswith(old_prefix):
+ paths_to_paths[dep] = re.sub(
+ re.escape(old_prefix), new_prefix, dep)
+ if dep.startswith('@'):
+ paths_to_paths[dep] = dep
+
+ return paths_to_paths
def modify_macho_object(cur_path, rpaths, deps, idpath,
- new_rpaths, new_deps, new_idpath):
+ paths_to_paths):
"""
- Modify MachO binary path_name by replacing old_dir with new_dir
- or the relative path to spack install root.
- The old install dir in LC_ID_DYLIB is replaced with the new install dir
- using install_name_tool -id newid binary
- The old install dir in LC_LOAD_DYLIB is replaced with the new install dir
- using install_name_tool -change old new binary
- The old install dir in LC_RPATH is replaced with the new install dir using
- install_name_tool -rpath old new binary
+ This function is used to make machO buildcaches on macOS by
+ replacing old paths with new paths using install_name_tool
+ Inputs:
+ mach-o binary to be modified
+ original rpaths
+ original dependency paths
+ original id path if a mach-o library
+ dictionary mapping paths in old install layout to new install layout
"""
# avoid error message for libgcc_s
if 'libgcc_' in cur_path:
return
install_name_tool = Executable('install_name_tool')
- if new_idpath and not idpath == new_idpath:
- install_name_tool('-id', new_idpath, str(cur_path))
-
- if len(deps) == len(new_deps):
- for orig, new in zip(deps, new_deps):
- if not orig == new:
- install_name_tool('-change', orig, new, str(cur_path))
-
- if len(rpaths) == len(new_rpaths):
- for orig, new in zip(rpaths, new_rpaths):
- if not orig == new:
- install_name_tool('-rpath', orig, new, str(cur_path))
+ if idpath:
+ new_idpath = paths_to_paths.get(idpath, None)
+ if new_idpath and not idpath == new_idpath:
+ install_name_tool('-id', new_idpath, str(cur_path))
+ for dep in deps:
+ new_dep = paths_to_paths.get(dep)
+ if new_dep and dep != new_dep:
+ install_name_tool('-change', dep, new_dep, str(cur_path))
+
+ for orig_rpath in rpaths:
+ new_rpath = paths_to_paths.get(orig_rpath)
+ if new_rpath and not orig_rpath == new_rpath:
+ install_name_tool('-rpath', orig_rpath, new_rpath, str(cur_path))
return
-def modify_object_macholib(cur_path, old_dir, new_dir):
+def modify_object_macholib(cur_path, paths_to_paths):
"""
- Modify MachO binary path_name by replacing old_dir with new_dir
- or the relative path to spack install root.
- The old install dir in LC_ID_DYLIB is replaced with the new install dir
- using py-macholib
- The old install dir in LC_LOAD_DYLIB is replaced with the new install dir
- using py-macholib
- The old install dir in LC_RPATH is replaced with the new install dir using
- using py-macholib
+ This function is used when install machO buildcaches on linux by
+ rewriting mach-o loader commands for dependency library paths of
+ mach-o binaries and the id path for mach-o libraries.
+ Rewritting of rpaths is handled by replace_prefix_bin.
+ Inputs
+ mach-o binary to be modified
+ dictionary mapping paths in old install layout to new install layout
"""
- if cur_path.endswith('.o'):
- return
- try:
- from macholib.MachO import MachO
- except ImportError as e:
- raise MissingMacholibException(e)
-
- def match_func(cpath):
- rpath = cpath.replace(old_dir, new_dir)
- return rpath
dll = MachO(cur_path)
- dll.rewriteLoadCommands(match_func)
+
+ changedict = paths_to_paths
+
+ def changefunc(path):
+ npath = changedict.get(path, None)
+ return npath
+
+ dll.rewriteLoadCommands(changefunc)
+
try:
f = open(dll.filename, 'rb+')
for header in dll.headers:
@@ -320,14 +328,32 @@ def modify_object_macholib(cur_path, old_dir, new_dir):
return
-def strings_contains_installroot(path_name, root_dir):
+def macholib_get_paths(cur_path):
"""
- Check if the file contain the install root string.
+ Get rpaths, dependencies and id of mach-o objects
+ using python macholib package
"""
- strings = Executable('strings')
- output = strings('%s' % path_name,
- output=str, err=str)
- return (root_dir in output or spack.paths.prefix in output)
+ dll = MachO(cur_path)
+
+ ident = None
+ rpaths = list()
+ deps = list()
+ for header in dll.headers:
+ rpaths = [data.rstrip(b'\0').decode('utf-8')
+ for load_command, dylib_command, data in header.commands if
+ load_command.cmd == macholib.mach_o.LC_RPATH]
+ deps = [data.rstrip(b'\0').decode('utf-8')
+ for load_command, dylib_command, data in header.commands if
+ load_command.cmd == macholib.mach_o.LC_LOAD_DYLIB]
+ idents = [data.rstrip(b'\0').decode('utf-8')
+ for load_command, dylib_command, data in header.commands if
+ load_command.cmd == macholib.mach_o.LC_ID_DYLIB]
+ if len(idents) == 1:
+ ident = idents[0]
+ tty.debug('ident: %s' % ident)
+ tty.debug('deps: %s' % deps)
+ tty.debug('rpaths: %s' % rpaths)
+ return (rpaths, deps, ident)
def modify_elf_object(path_name, new_rpaths):
@@ -338,9 +364,9 @@ def modify_elf_object(path_name, new_rpaths):
new_joined = ':'.join(new_rpaths)
# if we're relocating patchelf itself, use it
+ bak_path = path_name + ".bak"
if path_name[-13:] == "/bin/patchelf":
- bak_path = path_name + ".bak"
shutil.copy(path_name, bak_path)
patchelf = Executable(bak_path)
else:
@@ -350,9 +376,11 @@ def modify_elf_object(path_name, new_rpaths):
patchelf('--force-rpath', '--set-rpath', '%s' % new_joined,
'%s' % path_name, output=str, error=str)
except ProcessError as e:
- tty.die('patchelf --set-rpath %s failed' %
- path_name, e)
+ msg = 'patchelf --set-rpath %s failed with error %s' % (path_name, e)
+ raise PatchelfError(msg)
pass
+ if os.path.exists(bak_path):
+ os.remove(bak_path)
def needs_binary_relocation(m_type, m_subtype):
@@ -447,11 +475,15 @@ def replace_prefix_nullterm(path_name, old_dir, new_dir):
return data
return match.group().replace(old_dir.encode('utf-8'),
new_dir.encode('utf-8')) + b'\0' * padding
+
+ if len(new_dir) > len(old_dir):
+ raise BinaryTextReplaceException(old_dir, new_dir)
+
with open(path_name, 'rb+') as f:
data = f.read()
f.seek(0)
original_data_len = len(data)
- pat = re.compile(old_dir.encode('utf-8') + b'([^\0]*?)\0')
+ pat = re.compile(re.escape(old_dir).encode('utf-8') + b'([^\0]*?)\0')
if not pat.search(data):
return
ndata = pat.sub(replace, data)
@@ -462,80 +494,129 @@ def replace_prefix_nullterm(path_name, old_dir, new_dir):
f.truncate()
-def relocate_macho_binaries(path_names, old_dir, new_dir, allow_root):
+def relocate_macho_binaries(path_names, old_layout_root, new_layout_root,
+ prefix_to_prefix, rel, old_prefix, new_prefix):
"""
- Change old_dir to new_dir in LC_RPATH of mach-o files (on macOS)
- Change old_dir to new_dir in LC_ID and LC_DEP of mach-o files
- Account for the case where old_dir is now a placeholder
+ Use macholib python package to get the rpaths, depedent libraries
+ and library identity for libraries from the MachO object. Modify them
+ with the replacement paths queried from the dictionary mapping old layout
+ prefixes to hashes and the dictionary mapping hashes to the new layout
+ prefixes.
"""
- placeholder = set_placeholder(old_dir)
+
for path_name in path_names:
+ # Corner case where macho object file ended up in the path name list
if path_name.endswith('.o'):
continue
- if new_dir == old_dir:
- continue
- if platform.system().lower() == 'darwin':
- rpaths, deps, idpath = macho_get_paths(path_name)
- # one pass to replace placeholder
- (n_rpaths,
- n_deps,
- n_idpath) = macho_replace_paths(placeholder,
- new_dir,
- rpaths,
- deps,
- idpath)
- # another pass to replace old_dir
- (new_rpaths,
- new_deps,
- new_idpath) = macho_replace_paths(old_dir,
- new_dir,
- n_rpaths,
- n_deps,
- n_idpath)
- modify_macho_object(path_name,
- rpaths, deps, idpath,
- new_rpaths, new_deps, new_idpath)
+ if rel:
+ # get the relativized paths
+ rpaths, deps, idpath = macholib_get_paths(path_name)
+ # get the file path name in the original prefix
+ orig_path_name = re.sub(re.escape(new_prefix), old_prefix,
+ path_name)
+ # get the mapping of the relativized paths to the original
+ # normalized paths
+ rel_to_orig = macho_make_paths_normal(orig_path_name,
+ rpaths, deps,
+ idpath)
+ # replace the relativized paths with normalized paths
+ if platform.system().lower() == 'darwin':
+ modify_macho_object(path_name, rpaths, deps,
+ idpath, rel_to_orig)
+ else:
+ modify_object_macholib(path_name,
+ rel_to_orig)
+ # get the normalized paths in the mach-o binary
+ rpaths, deps, idpath = macholib_get_paths(path_name)
+ # get the mapping of paths in old prefix to path in new prefix
+ paths_to_paths = macho_find_paths(rpaths, deps, idpath,
+ old_layout_root,
+ prefix_to_prefix)
+ # replace the old paths with new paths
+ if platform.system().lower() == 'darwin':
+ modify_macho_object(path_name, rpaths, deps,
+ idpath, paths_to_paths)
+ else:
+ modify_object_macholib(path_name,
+ paths_to_paths)
+ # get the new normalized path in the mach-o binary
+ rpaths, deps, idpath = macholib_get_paths(path_name)
+ # get the mapping of paths to relative paths in the new prefix
+ paths_to_paths = macho_make_paths_relative(path_name,
+ new_layout_root,
+ rpaths, deps, idpath)
+ # replace the new paths with relativized paths in the new prefix
+ if platform.system().lower() == 'darwin':
+ modify_macho_object(path_name, rpaths, deps,
+ idpath, paths_to_paths)
+ else:
+ modify_object_macholib(path_name,
+ paths_to_paths)
else:
- modify_object_macholib(path_name, placeholder, new_dir)
- modify_object_macholib(path_name, old_dir, new_dir)
- if len(new_dir) <= len(old_dir):
- replace_prefix_nullterm(path_name, old_dir, new_dir)
+ # get the paths in the old prefix
+ rpaths, deps, idpath = macholib_get_paths(path_name)
+ # get the mapping of paths in the old prerix to the new prefix
+ paths_to_paths = macho_find_paths(rpaths, deps, idpath,
+ old_layout_root,
+ prefix_to_prefix)
+ # replace the old paths with new paths
+ if platform.system().lower() == 'darwin':
+ modify_macho_object(path_name, rpaths, deps,
+ idpath, paths_to_paths)
+ else:
+ modify_object_macholib(path_name,
+ paths_to_paths)
+
+
+def elf_find_paths(orig_rpaths, old_layout_root, prefix_to_prefix):
+ new_rpaths = list()
+ for orig_rpath in orig_rpaths:
+ if orig_rpath.startswith(old_layout_root):
+ for old_prefix, new_prefix in prefix_to_prefix.items():
+ if orig_rpath.startswith(old_prefix):
+ new_rpaths.append(re.sub(re.escape(old_prefix),
+ new_prefix, orig_rpath))
else:
- tty.warn('Cannot do a binary string replacement'
- ' with padding for %s'
- ' because %s is longer than %s' %
- (path_name, new_dir, old_dir))
+ new_rpaths.append(orig_rpath)
+ return new_rpaths
-def relocate_elf_binaries(path_names, old_dir, new_dir, allow_root):
+def relocate_elf_binaries(path_names, old_layout_root, new_layout_root,
+ prefix_to_prefix, rel, old_prefix, new_prefix):
"""
- Change old_dir to new_dir in RPATHs of elf binaries
- Account for the case where old_dir is now a placeholder
+ Use patchelf to get the original rpaths and then replace them with
+ rpaths in the new directory layout.
+ New rpaths are determined from a dictionary mapping the prefixes in the
+ old directory layout to the prefixes in the new directory layout if the
+ rpath was in the old layout root, i.e. system paths are not replaced.
"""
- placeholder = set_placeholder(old_dir)
for path_name in path_names:
orig_rpaths = get_existing_elf_rpaths(path_name)
- if orig_rpaths:
- # one pass to replace placeholder
- n_rpaths = substitute_rpath(orig_rpaths,
- placeholder, new_dir)
- # one pass to replace old_dir
- new_rpaths = substitute_rpath(n_rpaths,
- old_dir, new_dir)
+ new_rpaths = list()
+ if rel:
+ # get the file path in the old_prefix
+ orig_path_name = re.sub(re.escape(new_prefix), old_prefix,
+ path_name)
+ # get the normalized rpaths in the old prefix using the file path
+ # in the orig prefix
+ orig_norm_rpaths = get_normalized_elf_rpaths(orig_path_name,
+ orig_rpaths)
+ # get the normalize rpaths in the new prefix
+ norm_rpaths = elf_find_paths(orig_norm_rpaths, old_layout_root,
+ prefix_to_prefix)
+ # get the relativized rpaths in the new prefix
+ new_rpaths = get_relative_elf_rpaths(path_name, new_layout_root,
+ norm_rpaths)
+ modify_elf_object(path_name, new_rpaths)
+ else:
+ new_rpaths = elf_find_paths(orig_rpaths, old_layout_root,
+ prefix_to_prefix)
modify_elf_object(path_name, new_rpaths)
- if not new_dir == old_dir:
- if len(new_dir) <= len(old_dir):
- replace_prefix_bin(path_name, old_dir, new_dir)
- else:
- tty.warn('Cannot do a binary string replacement'
- ' with padding for %s'
- ' because %s is longer than %s.' %
- (path_name, new_dir, old_dir))
def make_link_relative(cur_path_names, orig_path_names):
"""
- Change absolute links to be relative.
+ Change absolute links to relative links.
"""
for cur_path, orig_path in zip(cur_path_names, orig_path_names):
target = os.readlink(orig_path)
@@ -545,8 +626,8 @@ def make_link_relative(cur_path_names, orig_path_names):
os.symlink(relative_target, cur_path)
-def make_macho_binaries_relative(cur_path_names, orig_path_names, old_dir,
- allow_root):
+def make_macho_binaries_relative(cur_path_names, orig_path_names,
+ old_layout_root):
"""
Replace old RPATHs with paths relative to old_dir in binary files
"""
@@ -555,33 +636,26 @@ def make_macho_binaries_relative(cur_path_names, orig_path_names, old_dir,
deps = set()
idpath = None
if platform.system().lower() == 'darwin':
- (rpaths, deps, idpath) = macho_get_paths(cur_path)
- (new_rpaths,
- new_deps,
- new_idpath) = macho_make_paths_relative(orig_path, old_dir,
- rpaths, deps, idpath)
+ (rpaths, deps, idpath) = macholib_get_paths(cur_path)
+ paths_to_paths = macho_make_paths_relative(orig_path,
+ old_layout_root,
+ rpaths, deps, idpath)
modify_macho_object(cur_path,
rpaths, deps, idpath,
- new_rpaths, new_deps, new_idpath)
- if (not allow_root and
- not file_is_relocatable(cur_path)):
- raise InstallRootStringException(cur_path, old_dir)
+ paths_to_paths)
-def make_elf_binaries_relative(cur_path_names, orig_path_names, old_dir,
- allow_root):
+def make_elf_binaries_relative(cur_path_names, orig_path_names,
+ old_layout_root):
"""
Replace old RPATHs with paths relative to old_dir in binary files
"""
for cur_path, orig_path in zip(cur_path_names, orig_path_names):
orig_rpaths = get_existing_elf_rpaths(cur_path)
if orig_rpaths:
- new_rpaths = get_relative_rpaths(orig_path, old_dir,
- orig_rpaths)
+ new_rpaths = get_relative_elf_rpaths(orig_path, old_layout_root,
+ orig_rpaths)
modify_elf_object(cur_path, new_rpaths)
- if (not allow_root and
- not file_is_relocatable(cur_path)):
- raise InstallRootStringException(cur_path, old_dir)
def check_files_relocatable(cur_path_names, allow_root):
@@ -595,63 +669,74 @@ def check_files_relocatable(cur_path_names, allow_root):
cur_path, spack.store.layout.root)
-def make_link_placeholder(cur_path_names, cur_dir, old_dir):
- """
- Replace old install path with placeholder in absolute links.
-
- Links in ``cur_path_names`` must link to absolute paths.
- """
- for cur_path in cur_path_names:
- placeholder = set_placeholder(spack.store.layout.root)
- placeholder_prefix = old_dir.replace(spack.store.layout.root,
- placeholder)
- cur_src = os.readlink(cur_path)
- rel_src = os.path.relpath(cur_src, cur_dir)
- new_src = os.path.join(placeholder_prefix, rel_src)
-
- os.unlink(cur_path)
- os.symlink(new_src, cur_path)
+def relocate_links(linknames, old_layout_root, new_layout_root,
+ old_install_prefix, new_install_prefix, prefix_to_prefix):
+ """
+ The symbolic links in filenames are absolute links or placeholder links.
+ The old link target is read and the placeholder is replaced by the old
+ layout root. If the old link target is in the old install prefix, the new
+ link target is create by replacing the old install prefix with the new
+ install prefix.
+ """
+ placeholder = set_placeholder(old_layout_root)
+ link_names = [os.path.join(new_install_prefix, linkname)
+ for linkname in linknames]
+ for link_name in link_names:
+ old_link_target = os.readlink(link_name)
+ old_link_target = re.sub(placeholder, old_layout_root, old_link_target)
+ if old_link_target.startswith(old_install_prefix):
+ new_link_target = re.sub(
+ old_install_prefix, new_install_prefix, old_link_target)
+ os.unlink(link_name)
+ os.symlink(new_link_target, link_name)
+ else:
+ msg = 'Old link target %s' % old_link_target
+ msg += ' for symbolic link %s is outside' % link_name
+ msg += ' of the old install prefix %s.\n' % old_install_prefix
+ msg += 'This symbolic link will not be relocated'
+ msg += ' and might break relocation.'
+ tty.warn(msg)
-def relocate_links(path_names, old_dir, new_dir):
+def relocate_text(path_names, old_layout_root, new_layout_root,
+ old_install_prefix, new_install_prefix,
+ old_spack_prefix, new_spack_prefix,
+ prefix_to_prefix):
"""
- Replace old path with new path in link sources.
-
- Links in ``path_names`` must link to absolute paths or placeholders.
+ Replace old paths with new paths in text files
+ including the path the the spack sbang script
"""
- placeholder = set_placeholder(old_dir)
- for path_name in path_names:
- old_src = os.readlink(path_name)
- # replace either placeholder or old_dir
- new_src = old_src.replace(placeholder, new_dir, 1)
- new_src = new_src.replace(old_dir, new_dir, 1)
-
- os.unlink(path_name)
- os.symlink(new_src, path_name)
+ sbangre = '#!/bin/bash %s/bin/sbang' % old_spack_prefix
+ sbangnew = '#!/bin/bash %s/bin/sbang' % new_spack_prefix
-
-def relocate_text(path_names, oldpath, newpath, oldprefix, newprefix):
- """
- Replace old path with new path in text files
- including the path the the spack sbang script.
- """
- sbangre = '#!/bin/bash %s/bin/sbang' % oldprefix
- sbangnew = '#!/bin/bash %s/bin/sbang' % newprefix
for path_name in path_names:
- replace_prefix_text(path_name, oldpath, newpath)
+ replace_prefix_text(path_name, old_install_prefix, new_install_prefix)
+ for orig_dep_prefix, new_dep_prefix in prefix_to_prefix.items():
+ replace_prefix_text(path_name, orig_dep_prefix, new_dep_prefix)
+ replace_prefix_text(path_name, old_layout_root, new_layout_root)
replace_prefix_text(path_name, sbangre, sbangnew)
- replace_prefix_text(path_name, oldprefix, newprefix)
-def substitute_rpath(orig_rpath, topdir, new_root_path):
- """
- Replace topdir with new_root_path RPATH list orig_rpath
- """
- new_rpaths = []
- for path in orig_rpath:
- new_rpath = path.replace(topdir, new_root_path)
- new_rpaths.append(new_rpath)
- return new_rpaths
+def relocate_text_bin(path_names, old_layout_root, new_layout_root,
+ old_install_prefix, new_install_prefix,
+ old_spack_prefix, new_spack_prefix,
+ prefix_to_prefix):
+ """
+ Replace null terminated path strings hard coded into binaries.
+ Raise an exception when the new path in longer than the old path
+ because this breaks the binary.
+ """
+ if len(new_install_prefix) <= len(old_install_prefix):
+ for path_name in path_names:
+ for old_dep_prefix, new_dep_prefix in prefix_to_prefix.items():
+ if len(new_dep_prefix) <= len(old_dep_prefix):
+ replace_prefix_bin(
+ path_name, old_dep_prefix, new_dep_prefix)
+ replace_prefix_bin(path_name, old_spack_prefix, new_spack_prefix)
+ else:
+ if len(path_names) > 0:
+ raise BinaryTextReplaceException(
+ old_install_prefix, new_install_prefix)
def is_relocatable(spec):
@@ -729,7 +814,7 @@ def file_is_relocatable(file, paths_to_relocate=None):
set_of_strings.discard(rpaths)
if platform.system().lower() == 'darwin':
if m_subtype == 'x-mach-binary':
- rpaths, deps, idpath = macho_get_paths(file)
+ rpaths, deps, idpath = macholib_get_paths(file)
set_of_strings.discard(set(rpaths))
set_of_strings.discard(set(deps))
if idpath is not None:
@@ -779,6 +864,8 @@ def mime_type(file):
file_cmd = Executable('file')
output = file_cmd('-b', '-h', '--mime-type', file, output=str, error=str)
tty.debug('[MIME_TYPE] {0} -> {1}'.format(file, output.strip()))
+ # In corner cases the output does not contain a subtype prefixed with a /
+ # In those cases add the / so the tuple can be formed.
if '/' not in output:
output += '/'
split_by_slash = output.strip().split('/')
diff --git a/lib/spack/spack/test/packaging.py b/lib/spack/spack/test/packaging.py
index 39d12df7b7..5d5b2ccf8b 100644
--- a/lib/spack/spack/test/packaging.py
+++ b/lib/spack/spack/test/packaging.py
@@ -8,10 +8,11 @@ This test checks the binary packaging infrastructure
"""
import os
import stat
-import sys
import shutil
import pytest
import argparse
+import re
+import platform
from llnl.util.filesystem import mkdirp
@@ -19,16 +20,15 @@ import spack.repo
import spack.store
import spack.binary_distribution as bindist
import spack.cmd.buildcache as buildcache
-import spack.util.gpg
from spack.spec import Spec
from spack.paths import mock_gpg_keys_path
from spack.fetch_strategy import URLFetchStrategy, FetchStrategyComposite
from spack.relocate import needs_binary_relocation, needs_text_relocation
-from spack.relocate import strings_contains_installroot
-from spack.relocate import get_patchelf, relocate_text, relocate_links
-from spack.relocate import substitute_rpath, get_relative_rpaths
-from spack.relocate import macho_replace_paths, macho_make_paths_relative
-from spack.relocate import modify_macho_object, macho_get_paths
+from spack.relocate import relocate_text, relocate_links
+from spack.relocate import get_relative_elf_rpaths
+from spack.relocate import macho_make_paths_relative
+from spack.relocate import set_placeholder, macho_find_paths
+from spack.relocate import file_is_relocatable
def has_gpg():
@@ -50,9 +50,9 @@ def fake_fetchify(url, pkg):
@pytest.mark.usefixtures('install_mockery', 'mock_gnupghome')
def test_buildcache(mock_archive, tmpdir):
# tweak patchelf to only do a download
- spec = Spec("patchelf")
- spec.concretize()
- pkg = spack.repo.get(spec)
+ pspec = Spec("patchelf")
+ pspec.concretize()
+ pkg = spack.repo.get(pspec)
fake_fetchify(pkg.fetcher, pkg)
mkdirp(os.path.join(pkg.prefix, "bin"))
patchelfscr = os.path.join(pkg.prefix, "bin", "patchelf")
@@ -71,7 +71,7 @@ echo $PATH"""
pkg = spec.package
fake_fetchify(mock_archive.url, pkg)
pkg.do_install()
- pkghash = '/' + spec.dag_hash(7)
+ pkghash = '/' + str(spec.dag_hash(7))
# Put some non-relocatable file in there
filename = os.path.join(spec.prefix, "dummy.txt")
@@ -99,88 +99,69 @@ echo $PATH"""
parser = argparse.ArgumentParser()
buildcache.setup_parser(parser)
+ create_args = ['create', '-a', '-f', '-d', mirror_path, pkghash]
# Create a private key to sign package with if gpg2 available
if spack.util.gpg.Gpg.gpg():
spack.util.gpg.Gpg.create(name='test key 1', expires='0',
email='spack@googlegroups.com',
comment='Spack test key')
- # Create build cache with signing
- args = parser.parse_args(['create', '-d', mirror_path, str(spec)])
- buildcache.buildcache(parser, args)
-
- # Uninstall the package
- pkg.do_uninstall(force=True)
-
- # test overwrite install
- args = parser.parse_args(['install', '-f', str(pkghash)])
- buildcache.buildcache(parser, args)
-
- files = os.listdir(spec.prefix)
+ else:
+ create_args.insert(create_args.index('-a'), '-u')
- # create build cache with relative path and signing
- args = parser.parse_args(
- ['create', '-d', mirror_path, '-f', '-r', str(spec)])
- buildcache.buildcache(parser, args)
+ args = parser.parse_args(create_args)
+ buildcache.buildcache(parser, args)
+ # trigger overwrite warning
+ buildcache.buildcache(parser, args)
- # Uninstall the package
- pkg.do_uninstall(force=True)
+ # Uninstall the package
+ pkg.do_uninstall(force=True)
- # install build cache with verification
- args = parser.parse_args(['install', str(spec)])
- buildcache.install_tarball(spec, args)
+ install_args = ['install', '-a', '-f', pkghash]
+ if not spack.util.gpg.Gpg.gpg():
+ install_args.insert(install_args.index('-a'), '-u')
+ args = parser.parse_args(install_args)
+ # Test install
+ buildcache.buildcache(parser, args)
- # test overwrite install
- args = parser.parse_args(['install', '-f', str(pkghash)])
- buildcache.buildcache(parser, args)
+ files = os.listdir(spec.prefix)
- else:
- # create build cache without signing
- args = parser.parse_args(
- ['create', '-d', mirror_path, '-f', '-u', str(spec)])
- buildcache.buildcache(parser, args)
-
- # Uninstall the package
- pkg.do_uninstall(force=True)
-
- # install build cache without verification
- args = parser.parse_args(['install', '-u', str(spec)])
- buildcache.install_tarball(spec, args)
-
- files = os.listdir(spec.prefix)
- assert 'link_to_dummy.txt' in files
- assert 'dummy.txt' in files
- # test overwrite install without verification
- args = parser.parse_args(['install', '-f', '-u', str(pkghash)])
- buildcache.buildcache(parser, args)
-
- # create build cache with relative path
- args = parser.parse_args(
- ['create', '-d', mirror_path, '-f', '-r', '-u', str(pkghash)])
- buildcache.buildcache(parser, args)
-
- # Uninstall the package
- pkg.do_uninstall(force=True)
-
- # install build cache
- args = parser.parse_args(['install', '-u', str(spec)])
- buildcache.install_tarball(spec, args)
-
- # test overwrite install
- args = parser.parse_args(['install', '-f', '-u', str(pkghash)])
- buildcache.buildcache(parser, args)
-
- files = os.listdir(spec.prefix)
- assert 'link_to_dummy.txt' in files
- assert 'dummy.txt' in files
- assert os.path.realpath(
- os.path.join(spec.prefix, 'link_to_dummy.txt')
- ) == os.path.realpath(os.path.join(spec.prefix, 'dummy.txt'))
+ assert 'link_to_dummy.txt' in files
+ assert 'dummy.txt' in files
# Validate the relocation information
buildinfo = bindist.read_buildinfo_file(spec.prefix)
assert(buildinfo['relocate_textfiles'] == ['dummy.txt'])
assert(buildinfo['relocate_links'] == ['link_to_dummy.txt'])
+ # create build cache with relative path
+ create_args.insert(create_args.index('-a'), '-f')
+ create_args.insert(create_args.index('-a'), '-r')
+ args = parser.parse_args(create_args)
+ buildcache.buildcache(parser, args)
+
+ # Uninstall the package
+ pkg.do_uninstall(force=True)
+
+ if not spack.util.gpg.Gpg.gpg():
+ install_args.insert(install_args.index('-a'), '-u')
+ args = parser.parse_args(install_args)
+ buildcache.buildcache(parser, args)
+
+ # test overwrite install
+ install_args.insert(install_args.index('-a'), '-f')
+ args = parser.parse_args(install_args)
+ buildcache.buildcache(parser, args)
+
+ files = os.listdir(spec.prefix)
+ assert 'link_to_dummy.txt' in files
+ assert 'dummy.txt' in files
+# assert os.path.realpath(
+# os.path.join(spec.prefix, 'link_to_dummy.txt')
+# ) == os.path.realpath(os.path.join(spec.prefix, 'dummy.txt'))
+
+ args = parser.parse_args(['keys'])
+ buildcache.buildcache(parser, args)
+
args = parser.parse_args(['list'])
buildcache.buildcache(parser, args)
@@ -200,6 +181,9 @@ echo $PATH"""
args = parser.parse_args(['keys', '-f'])
buildcache.buildcache(parser, args)
+ args = parser.parse_args(['keys', '-i', '-t'])
+ buildcache.buildcache(parser, args)
+
# unregister mirror with spack config
mirrors = {}
spack.config.set('mirrors', mirrors)
@@ -210,7 +194,10 @@ echo $PATH"""
bindist._cached_specs = set()
+@pytest.mark.usefixtures('install_mockery')
def test_relocate_text(tmpdir):
+ spec = Spec('trivial-install-test-package')
+ spec.concretize()
with tmpdir.as_cwd():
# Validate the text path replacement
old_dir = '/home/spack/opt/spack'
@@ -220,24 +207,46 @@ def test_relocate_text(tmpdir):
script.close()
filenames = [filename]
new_dir = '/opt/rh/devtoolset/'
- relocate_text(filenames, oldpath=old_dir, newpath=new_dir,
- oldprefix=old_dir, newprefix=new_dir)
+ relocate_text(filenames, old_dir, new_dir,
+ old_dir, new_dir,
+ old_dir, new_dir,
+ {old_dir: new_dir})
with open(filename, "r")as script:
for line in script:
assert(new_dir in line)
- assert(strings_contains_installroot(filename, old_dir) is False)
+ assert(file_is_relocatable(os.path.realpath(filename)))
+ # Remove cached binary specs since we deleted the mirror
+ bindist._cached_specs = set()
def test_relocate_links(tmpdir):
with tmpdir.as_cwd():
- old_dir = '/home/spack/opt/spack'
- filename = 'link.ln'
- old_src = os.path.join(old_dir, filename)
- os.symlink(old_src, filename)
- filenames = [filename]
- new_dir = '/opt/rh/devtoolset'
- relocate_links(filenames, old_dir, new_dir)
- assert os.path.realpath(filename) == os.path.join(new_dir, filename)
+ old_layout_root = os.path.join(
+ '%s' % tmpdir, 'home', 'spack', 'opt', 'spack')
+ old_install_prefix = os.path.join(
+ '%s' % old_layout_root, 'debian6', 'test')
+ old_binname = os.path.join(old_install_prefix, 'binfile')
+ placeholder = set_placeholder(old_layout_root)
+ re.sub(old_layout_root, placeholder, old_binname)
+ filenames = ['link.ln', 'outsideprefix.ln']
+ new_layout_root = os.path.join(
+ '%s' % tmpdir, 'opt', 'rh', 'devtoolset')
+ new_install_prefix = os.path.join(
+ '%s' % new_layout_root, 'test', 'debian6')
+ new_linkname = os.path.join(new_install_prefix, 'link.ln')
+ new_linkname2 = os.path.join(new_install_prefix, 'outsideprefix.ln')
+ new_binname = os.path.join(new_install_prefix, 'binfile')
+ mkdirp(new_install_prefix)
+ with open(new_binname, 'w') as f:
+ f.write('\n')
+ os.utime(new_binname, None)
+ os.symlink(old_binname, new_linkname)
+ os.symlink('/usr/lib/libc.so', new_linkname2)
+ relocate_links(filenames, old_layout_root, new_layout_root,
+ old_install_prefix, new_install_prefix,
+ {old_install_prefix: new_install_prefix})
+ assert os.readlink(new_linkname) == new_binname
+ assert os.readlink(new_linkname2) == '/usr/lib/libc.so'
def test_needs_relocation():
@@ -246,15 +255,222 @@ def test_needs_relocation():
assert needs_binary_relocation('application', 'x-executable')
assert not needs_binary_relocation('application', 'x-octet-stream')
assert not needs_binary_relocation('text', 'x-')
-
assert needs_text_relocation('text', 'x-')
assert not needs_text_relocation('symbolic link to', 'x-')
assert needs_binary_relocation('application', 'x-mach-binary')
-def test_macho_paths():
-
+def test_replace_paths(tmpdir):
+ with tmpdir.as_cwd():
+ suffix = 'dylib' if platform.system().lower() == 'darwin' else 'so'
+ hash_a = '53moz6jwnw3xpiztxwhc4us26klribws'
+ hash_b = 'tk62dzu62kd4oh3h3heelyw23hw2sfee'
+ hash_c = 'hdkhduizmaddpog6ewdradpobnbjwsjl'
+ hash_d = 'hukkosc7ahff7o65h6cdhvcoxm57d4bw'
+ hash_loco = 'zy4oigsc4eovn5yhr2lk4aukwzoespob'
+
+ prefix2hash = dict()
+
+ old_spack_dir = os.path.join('%s' % tmpdir,
+ 'Users', 'developer', 'spack')
+ mkdirp(old_spack_dir)
+
+ oldprefix_a = os.path.join('%s' % old_spack_dir, 'pkgA-%s' % hash_a)
+ oldlibdir_a = os.path.join('%s' % oldprefix_a, 'lib')
+ mkdirp(oldlibdir_a)
+ prefix2hash[str(oldprefix_a)] = hash_a
+
+ oldprefix_b = os.path.join('%s' % old_spack_dir, 'pkgB-%s' % hash_b)
+ oldlibdir_b = os.path.join('%s' % oldprefix_b, 'lib')
+ mkdirp(oldlibdir_b)
+ prefix2hash[str(oldprefix_b)] = hash_b
+
+ oldprefix_c = os.path.join('%s' % old_spack_dir, 'pkgC-%s' % hash_c)
+ oldlibdir_c = os.path.join('%s' % oldprefix_c, 'lib')
+ oldlibdir_cc = os.path.join('%s' % oldlibdir_c, 'C')
+ mkdirp(oldlibdir_c)
+ prefix2hash[str(oldprefix_c)] = hash_c
+
+ oldprefix_d = os.path.join('%s' % old_spack_dir, 'pkgD-%s' % hash_d)
+ oldlibdir_d = os.path.join('%s' % oldprefix_d, 'lib')
+ mkdirp(oldlibdir_d)
+ prefix2hash[str(oldprefix_d)] = hash_d
+
+ oldprefix_local = os.path.join('%s' % tmpdir, 'usr', 'local')
+ oldlibdir_local = os.path.join('%s' % oldprefix_local, 'lib')
+ mkdirp(oldlibdir_local)
+ prefix2hash[str(oldprefix_local)] = hash_loco
+ libfile_a = 'libA.%s' % suffix
+ libfile_b = 'libB.%s' % suffix
+ libfile_c = 'libC.%s' % suffix
+ libfile_d = 'libD.%s' % suffix
+ libfile_loco = 'libloco.%s' % suffix
+ old_libnames = [os.path.join(oldlibdir_a, libfile_a),
+ os.path.join(oldlibdir_b, libfile_b),
+ os.path.join(oldlibdir_c, libfile_c),
+ os.path.join(oldlibdir_d, libfile_d),
+ os.path.join(oldlibdir_local, libfile_loco)]
+
+ for old_libname in old_libnames:
+ with open(old_libname, 'a'):
+ os.utime(old_libname, None)
+
+ hash2prefix = dict()
+
+ new_spack_dir = os.path.join('%s' % tmpdir, 'Users', 'Shared',
+ 'spack')
+ mkdirp(new_spack_dir)
+
+ prefix_a = os.path.join(new_spack_dir, 'pkgA-%s' % hash_a)
+ libdir_a = os.path.join(prefix_a, 'lib')
+ mkdirp(libdir_a)
+ hash2prefix[hash_a] = str(prefix_a)
+
+ prefix_b = os.path.join(new_spack_dir, 'pkgB-%s' % hash_b)
+ libdir_b = os.path.join(prefix_b, 'lib')
+ mkdirp(libdir_b)
+ hash2prefix[hash_b] = str(prefix_b)
+
+ prefix_c = os.path.join(new_spack_dir, 'pkgC-%s' % hash_c)
+ libdir_c = os.path.join(prefix_c, 'lib')
+ libdir_cc = os.path.join(libdir_c, 'C')
+ mkdirp(libdir_cc)
+ hash2prefix[hash_c] = str(prefix_c)
+
+ prefix_d = os.path.join(new_spack_dir, 'pkgD-%s' % hash_d)
+ libdir_d = os.path.join(prefix_d, 'lib')
+ mkdirp(libdir_d)
+ hash2prefix[hash_d] = str(prefix_d)
+
+ prefix_local = os.path.join('%s' % tmpdir, 'usr', 'local')
+ libdir_local = os.path.join(prefix_local, 'lib')
+ mkdirp(libdir_local)
+ hash2prefix[hash_loco] = str(prefix_local)
+
+ new_libnames = [os.path.join(libdir_a, libfile_a),
+ os.path.join(libdir_b, libfile_b),
+ os.path.join(libdir_cc, libfile_c),
+ os.path.join(libdir_d, libfile_d),
+ os.path.join(libdir_local, libfile_loco)]
+
+ for new_libname in new_libnames:
+ with open(new_libname, 'a'):
+ os.utime(new_libname, None)
+
+ prefix2prefix = dict()
+ for prefix, hash in prefix2hash.items():
+ prefix2prefix[prefix] = hash2prefix[hash]
+
+ out_dict = macho_find_paths([oldlibdir_a, oldlibdir_b,
+ oldlibdir_c,
+ oldlibdir_cc, oldlibdir_local],
+ [os.path.join(oldlibdir_a,
+ libfile_a),
+ os.path.join(oldlibdir_b,
+ libfile_b),
+ os.path.join(oldlibdir_local,
+ libfile_loco)],
+ os.path.join(oldlibdir_cc,
+ libfile_c),
+ old_spack_dir,
+ prefix2prefix
+ )
+ assert out_dict == {oldlibdir_a: libdir_a,
+ oldlibdir_b: libdir_b,
+ oldlibdir_c: libdir_c,
+ oldlibdir_cc: libdir_cc,
+ libdir_local: libdir_local,
+ os.path.join(oldlibdir_a, libfile_a):
+ os.path.join(libdir_a, libfile_a),
+ os.path.join(oldlibdir_b, libfile_b):
+ os.path.join(libdir_b, libfile_b),
+ os.path.join(oldlibdir_local, libfile_loco):
+ os.path.join(libdir_local, libfile_loco),
+ os.path.join(oldlibdir_cc, libfile_c):
+ os.path.join(libdir_cc, libfile_c)}
+
+ out_dict = macho_find_paths([oldlibdir_a, oldlibdir_b,
+ oldlibdir_c,
+ oldlibdir_cc,
+ oldlibdir_local],
+ [os.path.join(oldlibdir_a,
+ libfile_a),
+ os.path.join(oldlibdir_b,
+ libfile_b),
+ os.path.join(oldlibdir_cc,
+ libfile_c),
+ os.path.join(oldlibdir_local,
+ libfile_loco)],
+ None,
+ old_spack_dir,
+ prefix2prefix
+ )
+ assert out_dict == {oldlibdir_a: libdir_a,
+ oldlibdir_b: libdir_b,
+ oldlibdir_c: libdir_c,
+ oldlibdir_cc: libdir_cc,
+ libdir_local: libdir_local,
+ os.path.join(oldlibdir_a, libfile_a):
+ os.path.join(libdir_a, libfile_a),
+ os.path.join(oldlibdir_b, libfile_b):
+ os.path.join(libdir_b, libfile_b),
+ os.path.join(oldlibdir_local, libfile_loco):
+ os.path.join(libdir_local, libfile_loco),
+ os.path.join(oldlibdir_cc, libfile_c):
+ os.path.join(libdir_cc, libfile_c)}
+
+ out_dict = macho_find_paths([oldlibdir_a, oldlibdir_b,
+ oldlibdir_c, oldlibdir_cc,
+ oldlibdir_local],
+ ['@rpath/%s' % libfile_a,
+ '@rpath/%s' % libfile_b,
+ '@rpath/%s' % libfile_c,
+ '@rpath/%s' % libfile_loco],
+ None,
+ old_spack_dir,
+ prefix2prefix
+ )
+
+ assert out_dict == {'@rpath/%s' % libfile_a:
+ '@rpath/%s' % libfile_a,
+ '@rpath/%s' % libfile_b:
+ '@rpath/%s' % libfile_b,
+ '@rpath/%s' % libfile_c:
+ '@rpath/%s' % libfile_c,
+ '@rpath/%s' % libfile_loco:
+ '@rpath/%s' % libfile_loco,
+ oldlibdir_a: libdir_a,
+ oldlibdir_b: libdir_b,
+ oldlibdir_c: libdir_c,
+ oldlibdir_cc: libdir_cc,
+ libdir_local: libdir_local,
+ }
+
+ out_dict = macho_find_paths([oldlibdir_a,
+ oldlibdir_b,
+ oldlibdir_d,
+ oldlibdir_local],
+ ['@rpath/%s' % libfile_a,
+ '@rpath/%s' % libfile_b,
+ '@rpath/%s' % libfile_loco],
+ None,
+ old_spack_dir,
+ prefix2prefix)
+ assert out_dict == {'@rpath/%s' % libfile_a:
+ '@rpath/%s' % libfile_a,
+ '@rpath/%s' % libfile_b:
+ '@rpath/%s' % libfile_b,
+ '@rpath/%s' % libfile_loco:
+ '@rpath/%s' % libfile_loco,
+ oldlibdir_a: libdir_a,
+ oldlibdir_b: libdir_b,
+ oldlibdir_d: libdir_d,
+ libdir_local: libdir_local,
+ }
+
+
+def test_macho_make_paths():
out = macho_make_paths_relative('/Users/Shares/spack/pkgC/lib/libC.dylib',
'/Users/Shared/spack',
('/Users/Shared/spack/pkgA/lib',
@@ -264,13 +480,19 @@ def test_macho_paths():
'/Users/Shared/spack/pkgB/libB.dylib',
'/usr/local/lib/libloco.dylib'),
'/Users/Shared/spack/pkgC/lib/libC.dylib')
- assert out == (['@loader_path/../../../../Shared/spack/pkgA/lib',
- '@loader_path/../../../../Shared/spack/pkgB/lib',
- '/usr/local/lib'],
- ['@loader_path/../../../../Shared/spack/pkgA/libA.dylib',
- '@loader_path/../../../../Shared/spack/pkgB/libB.dylib',
- '/usr/local/lib/libloco.dylib'],
- '@rpath/libC.dylib')
+ assert out == {'/Users/Shared/spack/pkgA/lib':
+ '@loader_path/../../../../Shared/spack/pkgA/lib',
+ '/Users/Shared/spack/pkgB/lib':
+ '@loader_path/../../../../Shared/spack/pkgB/lib',
+ '/usr/local/lib': '/usr/local/lib',
+ '/Users/Shared/spack/pkgA/libA.dylib':
+ '@loader_path/../../../../Shared/spack/pkgA/libA.dylib',
+ '/Users/Shared/spack/pkgB/libB.dylib':
+ '@loader_path/../../../../Shared/spack/pkgB/libB.dylib',
+ '/usr/local/lib/libloco.dylib':
+ '/usr/local/lib/libloco.dylib',
+ '/Users/Shared/spack/pkgC/lib/libC.dylib':
+ '@rpath/libC.dylib'}
out = macho_make_paths_relative('/Users/Shared/spack/pkgC/bin/exeC',
'/Users/Shared/spack',
@@ -281,98 +503,21 @@ def test_macho_paths():
'/Users/Shared/spack/pkgB/libB.dylib',
'/usr/local/lib/libloco.dylib'), None)
- assert out == (['@loader_path/../../pkgA/lib',
- '@loader_path/../../pkgB/lib',
- '/usr/local/lib'],
- ['@loader_path/../../pkgA/libA.dylib',
- '@loader_path/../../pkgB/libB.dylib',
- '/usr/local/lib/libloco.dylib'], None)
-
- out = macho_replace_paths('/Users/Shared/spack',
- '/Applications/spack',
- ('/Users/Shared/spack/pkgA/lib',
- '/Users/Shared/spack/pkgB/lib',
- '/usr/local/lib'),
- ('/Users/Shared/spack/pkgA/libA.dylib',
- '/Users/Shared/spack/pkgB/libB.dylib',
- '/usr/local/lib/libloco.dylib'),
- '/Users/Shared/spack/pkgC/lib/libC.dylib')
- assert out == (['/Applications/spack/pkgA/lib',
- '/Applications/spack/pkgB/lib',
- '/usr/local/lib'],
- ['/Applications/spack/pkgA/libA.dylib',
- '/Applications/spack/pkgB/libB.dylib',
- '/usr/local/lib/libloco.dylib'],
- '/Applications/spack/pkgC/lib/libC.dylib')
-
- out = macho_replace_paths('/Users/Shared/spack',
- '/Applications/spack',
- ('/Users/Shared/spack/pkgA/lib',
- '/Users/Shared/spack/pkgB/lib',
- '/usr/local/lib'),
- ('/Users/Shared/spack/pkgA/libA.dylib',
- '/Users/Shared/spack/pkgB/libB.dylib',
- '/usr/local/lib/libloco.dylib'),
- None)
- assert out == (['/Applications/spack/pkgA/lib',
- '/Applications/spack/pkgB/lib',
- '/usr/local/lib'],
- ['/Applications/spack/pkgA/libA.dylib',
- '/Applications/spack/pkgB/libB.dylib',
- '/usr/local/lib/libloco.dylib'],
- None)
+ assert out == {'/Users/Shared/spack/pkgA/lib':
+ '@loader_path/../../pkgA/lib',
+ '/Users/Shared/spack/pkgB/lib':
+ '@loader_path/../../pkgB/lib',
+ '/usr/local/lib': '/usr/local/lib',
+ '/Users/Shared/spack/pkgA/libA.dylib':
+ '@loader_path/../../pkgA/libA.dylib',
+ '/Users/Shared/spack/pkgB/libB.dylib':
+ '@loader_path/../../pkgB/libB.dylib',
+ '/usr/local/lib/libloco.dylib':
+ '/usr/local/lib/libloco.dylib'}
def test_elf_paths():
- out = get_relative_rpaths(
+ out = get_relative_elf_rpaths(
'/usr/bin/test', '/usr',
('/usr/lib', '/usr/lib64', '/opt/local/lib'))
assert out == ['$ORIGIN/../lib', '$ORIGIN/../lib64', '/opt/local/lib']
-
- out = substitute_rpath(
- ('/usr/lib', '/usr/lib64', '/opt/local/lib'), '/usr', '/opt')
- assert out == ['/opt/lib', '/opt/lib64', '/opt/local/lib']
-
-
-@pytest.mark.skipif(sys.platform != 'darwin',
- reason="only works with Mach-o objects")
-def test_relocate_macho(tmpdir):
- with tmpdir.as_cwd():
-
- get_patchelf() # this does nothing on Darwin
-
- rpaths, deps, idpath = macho_get_paths('/bin/bash')
- nrpaths, ndeps, nid = macho_make_paths_relative('/bin/bash', '/usr',
- rpaths, deps, idpath)
- shutil.copyfile('/bin/bash', 'bash')
- modify_macho_object('bash',
- rpaths, deps, idpath,
- nrpaths, ndeps, nid)
-
- rpaths, deps, idpath = macho_get_paths('/bin/bash')
- nrpaths, ndeps, nid = macho_replace_paths('/usr', '/opt',
- rpaths, deps, idpath)
- shutil.copyfile('/bin/bash', 'bash')
- modify_macho_object('bash',
- rpaths, deps, idpath,
- nrpaths, ndeps, nid)
-
- path = '/usr/lib/libncurses.5.4.dylib'
- rpaths, deps, idpath = macho_get_paths(path)
- nrpaths, ndeps, nid = macho_make_paths_relative(path, '/usr',
- rpaths, deps, idpath)
- shutil.copyfile(
- '/usr/lib/libncurses.5.4.dylib', 'libncurses.5.4.dylib')
- modify_macho_object('libncurses.5.4.dylib',
- rpaths, deps, idpath,
- nrpaths, ndeps, nid)
-
- rpaths, deps, idpath = macho_get_paths(path)
- nrpaths, ndeps, nid = macho_replace_paths('/usr', '/opt',
- rpaths, deps, idpath)
- shutil.copyfile(
- '/usr/lib/libncurses.5.4.dylib', 'libncurses.5.4.dylib')
- modify_macho_object(
- 'libncurses.5.4.dylib',
- rpaths, deps, idpath,
- nrpaths, ndeps, nid)