Buildcache: Install into non-default directory layouts (#13797)
* Buildcache: Install into non-default directory layouts Store a dictionary mapping of original dependency prefixes to dependency hashes Use the loaded spec to grab the new dependency prefixes in the new directory layout. Map the original dependency prefixes to the new dependency prefixes using the dependency hashes. Use the dependency prefixes map to replace original rpaths with new rpaths preserving the order. For mach-o binaries, use the dependency prefixes map to replace the dependency library entires for libraries and executables and the replace the library id for libraries. On Linux, patchelf is used to replace the rpaths of elf binaries. On macOS, install_name_tool is used to replace the rpaths and dependency libraries of mach-o binaries and the id of mach-o libraries. On Linux, macholib is used to replace the dependency libraries of mach-o binaries and the id of mach-o libraries. Binary text with padding replacement is attempted for all binaries for the following paths: spack layout root spack prefix sbang script location dependency prefixes package prefix Text replacement is attempted for all text files using the paths above. Symbolic links to the absolute path of the package install prefix are replaced, all others produce warnings.
This commit is contained in:
parent
0301ec32b4
commit
17e4df1e41
9
lib/spack/external/altgraph/__init__.py
vendored
9
lib/spack/external/altgraph/__init__.py
vendored
@ -139,9 +139,12 @@
|
|||||||
@contributor: U{Reka Albert <http://www.phys.psu.edu/~ralbert/>}
|
@contributor: U{Reka Albert <http://www.phys.psu.edu/~ralbert/>}
|
||||||
|
|
||||||
'''
|
'''
|
||||||
import pkg_resources
|
# import pkg_resources
|
||||||
__version__ = pkg_resources.require('altgraph')[0].version
|
# __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):
|
class GraphError(ValueError):
|
||||||
pass
|
pass
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import glob
|
||||||
|
import platform
|
||||||
|
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
import ruamel.yaml as yaml
|
import ruamel.yaml as yaml
|
||||||
|
|
||||||
@ -53,7 +56,7 @@
|
|||||||
BUILD_CACHE_INDEX_ENTRY_TEMPLATE = ' <li><a href="{path}">{path}</a></li>'
|
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.
|
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
|
Raised when gpg2 is not in PATH
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
def __init__(self, msg):
|
||||||
|
super(NoGpgException, self).__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class NoKeyException(spack.error.SpackError):
|
class NoKeyException(spack.error.SpackError):
|
||||||
"""
|
"""
|
||||||
Raised when gpg has no default key added.
|
Raised when gpg has no default key added.
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
def __init__(self, msg):
|
||||||
|
super(NoKeyException, self).__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class PickKeyException(spack.error.SpackError):
|
class PickKeyException(spack.error.SpackError):
|
||||||
@ -84,7 +91,7 @@ class PickKeyException(spack.error.SpackError):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, keys):
|
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."
|
err_msg += "Use spack buildcache create -k <key hash> to pick a key."
|
||||||
super(PickKeyException, self).__init__(err_msg)
|
super(PickKeyException, self).__init__(err_msg)
|
||||||
|
|
||||||
@ -107,7 +114,9 @@ class NewLayoutException(spack.error.SpackError):
|
|||||||
"""
|
"""
|
||||||
Raised if directory layout is different from buildcache.
|
Raised if directory layout is different from buildcache.
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
def __init__(self, msg):
|
||||||
|
super(NewLayoutException, self).__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
def build_cache_relative_path():
|
def build_cache_relative_path():
|
||||||
@ -137,15 +146,21 @@ def read_buildinfo_file(prefix):
|
|||||||
return buildinfo
|
return buildinfo
|
||||||
|
|
||||||
|
|
||||||
def write_buildinfo_file(prefix, workdir, rel=False):
|
def write_buildinfo_file(spec, workdir, rel=False):
|
||||||
"""
|
"""
|
||||||
Create a cache file containing information
|
Create a cache file containing information
|
||||||
required for the relocation
|
required for the relocation
|
||||||
"""
|
"""
|
||||||
|
prefix = spec.prefix
|
||||||
text_to_relocate = []
|
text_to_relocate = []
|
||||||
binary_to_relocate = []
|
binary_to_relocate = []
|
||||||
link_to_relocate = []
|
link_to_relocate = []
|
||||||
blacklist = (".spack", "man")
|
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.
|
# Do this at during tarball creation to save time when tarball unpacked.
|
||||||
# Used by make_package_relative to determine binaries to change.
|
# Used by make_package_relative to determine binaries to change.
|
||||||
for root, dirs, files in os.walk(prefix, topdown=True):
|
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)
|
link_to_relocate.append(rel_path_name)
|
||||||
else:
|
else:
|
||||||
msg = 'Absolute link %s to %s ' % (path_name, link)
|
msg = 'Absolute link %s to %s ' % (path_name, link)
|
||||||
msg += 'outside of stage %s ' % prefix
|
msg += 'outside of prefix %s ' % prefix
|
||||||
msg += 'cannot be relocated.'
|
msg += 'should not be relocated.'
|
||||||
tty.warn(msg)
|
tty.warn(msg)
|
||||||
|
|
||||||
if relocate.needs_binary_relocation(m_type, m_subtype):
|
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_textfiles'] = text_to_relocate
|
||||||
buildinfo['relocate_binaries'] = binary_to_relocate
|
buildinfo['relocate_binaries'] = binary_to_relocate
|
||||||
buildinfo['relocate_links'] = link_to_relocate
|
buildinfo['relocate_links'] = link_to_relocate
|
||||||
|
buildinfo['prefix_to_hash'] = prefix_to_hash
|
||||||
filename = buildinfo_file_name(workdir)
|
filename = buildinfo_file_name(workdir)
|
||||||
with open(filename, 'w') as outfile:
|
with open(filename, 'w') as outfile:
|
||||||
outfile.write(syaml.dump(buildinfo, default_flow_style=True))
|
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)
|
os.remove(temp_tarfile_path)
|
||||||
|
|
||||||
# create info for later relocation and create tar
|
# 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
|
# optionally make the paths in the binaries relative to each other
|
||||||
# in the spack install tree before creating tarball
|
# 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)
|
tty.die(e)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
make_package_placeholder(workdir, spec, allow_root)
|
check_package_relocatable(workdir, spec, allow_root)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
shutil.rmtree(workdir)
|
shutil.rmtree(workdir)
|
||||||
shutil.rmtree(tarfile_dir)
|
shutil.rmtree(tarfile_dir)
|
||||||
@ -400,6 +416,7 @@ def build_tarball(spec, outdir, force=False, rel=False, unsigned=False,
|
|||||||
buildinfo = {}
|
buildinfo = {}
|
||||||
buildinfo['relative_prefix'] = os.path.relpath(
|
buildinfo['relative_prefix'] = os.path.relpath(
|
||||||
spec.prefix, spack.store.layout.root)
|
spec.prefix, spack.store.layout.root)
|
||||||
|
buildinfo['relative_rpaths'] = rel
|
||||||
spec_dict['buildinfo'] = buildinfo
|
spec_dict['buildinfo'] = buildinfo
|
||||||
spec_dict['full_hash'] = spec.full_hash()
|
spec_dict['full_hash'] = spec.full_hash()
|
||||||
|
|
||||||
@ -481,100 +498,149 @@ def make_package_relative(workdir, spec, allow_root):
|
|||||||
"""
|
"""
|
||||||
prefix = spec.prefix
|
prefix = spec.prefix
|
||||||
buildinfo = read_buildinfo_file(workdir)
|
buildinfo = read_buildinfo_file(workdir)
|
||||||
old_path = buildinfo['buildpath']
|
old_layout_root = buildinfo['buildpath']
|
||||||
orig_path_names = list()
|
orig_path_names = list()
|
||||||
cur_path_names = list()
|
cur_path_names = list()
|
||||||
for filename in buildinfo['relocate_binaries']:
|
for filename in buildinfo['relocate_binaries']:
|
||||||
orig_path_names.append(os.path.join(prefix, filename))
|
orig_path_names.append(os.path.join(prefix, filename))
|
||||||
cur_path_names.append(os.path.join(workdir, 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,
|
relocate.make_macho_binaries_relative(cur_path_names, orig_path_names,
|
||||||
old_path, allow_root)
|
old_layout_root)
|
||||||
else:
|
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,
|
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()
|
orig_path_names = list()
|
||||||
cur_path_names = list()
|
cur_path_names = list()
|
||||||
for filename in buildinfo.get('relocate_links', []):
|
for linkname in buildinfo.get('relocate_links', []):
|
||||||
orig_path_names.append(os.path.join(prefix, filename))
|
orig_path_names.append(os.path.join(prefix, linkname))
|
||||||
cur_path_names.append(os.path.join(workdir, filename))
|
cur_path_names.append(os.path.join(workdir, linkname))
|
||||||
relocate.make_link_relative(cur_path_names, orig_path_names)
|
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.
|
Check if package binaries are relocatable.
|
||||||
Change links to placeholder links.
|
Change links to placeholder links.
|
||||||
"""
|
"""
|
||||||
prefix = spec.prefix
|
|
||||||
buildinfo = read_buildinfo_file(workdir)
|
buildinfo = read_buildinfo_file(workdir)
|
||||||
cur_path_names = list()
|
cur_path_names = list()
|
||||||
for filename in buildinfo['relocate_binaries']:
|
for filename in buildinfo['relocate_binaries']:
|
||||||
cur_path_names.append(os.path.join(workdir, filename))
|
cur_path_names.append(os.path.join(workdir, filename))
|
||||||
relocate.check_files_relocatable(cur_path_names, allow_root)
|
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(spec, allow_root):
|
||||||
def relocate_package(workdir, spec, allow_root):
|
|
||||||
"""
|
"""
|
||||||
Relocate the given package
|
Relocate the given package
|
||||||
"""
|
"""
|
||||||
|
workdir = str(spec.prefix)
|
||||||
buildinfo = read_buildinfo_file(workdir)
|
buildinfo = read_buildinfo_file(workdir)
|
||||||
new_path = str(spack.store.layout.root)
|
new_layout_root = str(spack.store.layout.root)
|
||||||
new_prefix = str(spack.paths.prefix)
|
new_prefix = str(spec.prefix)
|
||||||
old_path = str(buildinfo['buildpath'])
|
new_rel_prefix = str(os.path.relpath(new_prefix, new_layout_root))
|
||||||
old_prefix = str(buildinfo.get('spackprefix',
|
new_spack_prefix = str(spack.paths.prefix)
|
||||||
'/not/in/buildinfo/dictionary'))
|
old_layout_root = str(buildinfo['buildpath'])
|
||||||
rel = buildinfo.get('relative_rpaths', False)
|
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.msg("Relocating package from",
|
tty.debug("Relocating package from",
|
||||||
"%s to %s." % (old_path, new_path))
|
"%s to %s." % (old_layout_root, new_layout_root))
|
||||||
path_names = set()
|
|
||||||
|
def is_backup_file(file):
|
||||||
|
return file.endswith('~')
|
||||||
|
|
||||||
|
# Text files containing the prefix text
|
||||||
|
text_names = list()
|
||||||
for filename in buildinfo['relocate_textfiles']:
|
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.
|
# Don't add backup files generated by filter_file during install step.
|
||||||
if not path_name.endswith('~'):
|
if not is_backup_file(text_name):
|
||||||
path_names.add(path_name)
|
text_names.append(text_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:
|
# If we are installing back to the same location don't replace anything
|
||||||
tty.debug('Cannot do a binary string replacement with padding '
|
if old_layout_root != new_layout_root:
|
||||||
'for package because %s is longer than %s.' %
|
paths_to_relocate = [old_spack_prefix, old_layout_root]
|
||||||
(new_path, old_path))
|
paths_to_relocate.extend(prefix_to_hash.keys())
|
||||||
else:
|
files_to_relocate = list(filter(
|
||||||
for path_name in files_to_relocate:
|
lambda pathname: not relocate.file_is_relocatable(
|
||||||
relocate.replace_prefix_bin(path_name, old_path, new_path)
|
pathname, paths_to_relocate=paths_to_relocate),
|
||||||
else:
|
map(lambda filename: os.path.join(workdir, filename),
|
||||||
path_names = set()
|
buildinfo['relocate_binaries'])))
|
||||||
for filename in buildinfo['relocate_binaries']:
|
# If the buildcache was not created with relativized rpaths
|
||||||
path_name = os.path.join(workdir, filename)
|
# do the relocation of path in binaries
|
||||||
path_names.add(path_name)
|
if (spec.architecture.platform == 'darwin' or
|
||||||
if spec.architecture.platform == 'darwin':
|
spec.architecture.platform == 'test' and
|
||||||
relocate.relocate_macho_binaries(path_names, old_path,
|
platform.system().lower() == 'darwin'):
|
||||||
new_path, allow_root)
|
relocate.relocate_macho_binaries(files_to_relocate,
|
||||||
else:
|
old_layout_root,
|
||||||
relocate.relocate_elf_binaries(path_names, old_path,
|
new_layout_root,
|
||||||
new_path, allow_root)
|
prefix_to_prefix, rel,
|
||||||
path_names = set()
|
old_prefix,
|
||||||
for filename in buildinfo.get('relocate_links', []):
|
new_prefix)
|
||||||
path_name = os.path.join(workdir, filename)
|
if (spec.architecture.platform == 'linux' or
|
||||||
path_names.add(path_name)
|
spec.architecture.platform == 'test' and
|
||||||
relocate.relocate_links(path_names, old_path, new_path)
|
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,
|
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)
|
Gpg.verify('%s.asc' % specfile_path, specfile_path, suppress)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
shutil.rmtree(tmpdir)
|
shutil.rmtree(tmpdir)
|
||||||
tty.die(e)
|
raise e
|
||||||
else:
|
else:
|
||||||
shutil.rmtree(tmpdir)
|
shutil.rmtree(tmpdir)
|
||||||
raise NoVerifyException(
|
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
|
# if the original relative prefix is in the spec file use it
|
||||||
buildinfo = spec_dict.get('buildinfo', {})
|
buildinfo = spec_dict.get('buildinfo', {})
|
||||||
old_relative_prefix = buildinfo.get('relative_prefix', new_relative_prefix)
|
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
|
# if the original relative prefix and new relative prefix differ the
|
||||||
# directory layout has changed and the buildcache cannot be installed
|
# directory layout has changed and the buildcache cannot be installed
|
||||||
if old_relative_prefix != new_relative_prefix:
|
# if it was created with relative rpaths
|
||||||
shutil.rmtree(tmpdir)
|
info = 'old relative prefix %s\nnew relative prefix %s\nrelative rpaths %s'
|
||||||
msg = "Package tarball was created from an install "
|
tty.debug(info %
|
||||||
msg += "prefix with a different directory layout.\n"
|
(old_relative_prefix, new_relative_prefix, rel))
|
||||||
msg += "It cannot be relocated."
|
# if (old_relative_prefix != new_relative_prefix and (rel)):
|
||||||
raise NewLayoutException(msg)
|
# 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
|
# extract the tarball in a temp directory
|
||||||
with closing(tarfile.open(tarfile_path, 'r')) as tar:
|
with closing(tarfile.open(tarfile_path, 'r')) as tar:
|
||||||
tar.extractall(path=tmpdir)
|
tar.extractall(path=tmpdir)
|
||||||
# the base of the install prefix is used when creating the tarball
|
# get the parent directory of the file .spack/binary_distribution
|
||||||
# so the pathname should be the same now that the directory layout
|
# this should the directory unpacked from the tarball whose
|
||||||
# is confirmed
|
# name is unknown because the prefix naming is unknown
|
||||||
workdir = os.path.join(tmpdir, os.path.basename(spec.prefix))
|
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
|
# install_tree copies hardlinks
|
||||||
# create a temporary tarfile from prefix and exract it to workdir
|
# create a temporary tarfile from prefix and exract it to workdir
|
||||||
# tarfile preserves hardlinks
|
# tarfile preserves hardlinks
|
||||||
@ -672,10 +746,10 @@ def extract_tarball(spec, filename, allow_root=False, unsigned=False,
|
|||||||
os.remove(specfile_path)
|
os.remove(specfile_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
relocate_package(spec.prefix, spec, allow_root)
|
relocate_package(spec, allow_root)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
shutil.rmtree(spec.prefix)
|
shutil.rmtree(spec.prefix)
|
||||||
tty.die(e)
|
raise e
|
||||||
else:
|
else:
|
||||||
manifest_file = os.path.join(spec.prefix,
|
manifest_file = os.path.join(spec.prefix,
|
||||||
spack.store.layout.metadata_dir,
|
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)
|
tty.warn('No manifest file in tarball for spec %s' % spec_id)
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(tmpdir)
|
shutil.rmtree(tmpdir)
|
||||||
|
if os.path.exists(filename):
|
||||||
|
os.remove(filename)
|
||||||
|
|
||||||
|
|
||||||
# Internal cache for downloaded specs
|
# Internal cache for downloaded specs
|
||||||
@ -732,7 +808,7 @@ def get_spec(spec=None, force=False):
|
|||||||
tty.debug("No Spack mirrors are currently configured")
|
tty.debug("No Spack mirrors are currently configured")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if spec in _cached_specs:
|
if _cached_specs and spec in _cached_specs:
|
||||||
return _cached_specs
|
return _cached_specs
|
||||||
|
|
||||||
for mirror in spack.mirror.MirrorCollection().values():
|
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)
|
mirror_dir = url_util.local_file_path(fetch_url_build_cache)
|
||||||
if mirror_dir:
|
if mirror_dir:
|
||||||
tty.msg("Finding public keys in %s" % 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:
|
for file in files:
|
||||||
if re.search(r'\.key', file) or re.search(r'\.pub', file):
|
if re.search(r'\.key', file) or re.search(r'\.pub', file):
|
||||||
link = url_util.join(fetch_url_build_cache, file)
|
link = url_util.join(fetch_url_build_cache, file)
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
import llnl.util.lang
|
import llnl.util.lang
|
||||||
from spack.util.executable import Executable, ProcessError
|
from spack.util.executable import Executable, ProcessError
|
||||||
import llnl.util.tty as tty
|
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):
|
class InstallRootStringException(spack.error.SpackError):
|
||||||
@ -41,45 +44,56 @@ def __init__(self, file_path, old_len, new_len):
|
|||||||
(file_path, old_len, new_len))
|
(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):
|
def __init__(self, error):
|
||||||
super(MissingMacholibException, self).__init__(
|
super(PatchelfError, self).__init__(error)
|
||||||
"%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)
|
|
||||||
|
|
||||||
|
|
||||||
def get_patchelf():
|
def get_patchelf():
|
||||||
"""
|
"""
|
||||||
|
Returns the full patchelf binary path if available in $PATH.
|
||||||
Builds and installs spack patchelf package on linux platforms
|
Builds and installs spack patchelf package on linux platforms
|
||||||
using the first concretized spec.
|
using the first concretized spec if it is not installed and
|
||||||
Returns the full patchelf binary path.
|
returns the full patchelf binary path.
|
||||||
"""
|
"""
|
||||||
# as we may need patchelf, find out where it is
|
# as we may need patchelf, find out where it is
|
||||||
patchelf = spack.util.executable.which('patchelf')
|
patchelf = spack.util.executable.which('patchelf')
|
||||||
if patchelf is not None:
|
if patchelf is not None:
|
||||||
return patchelf.path
|
return patchelf.path
|
||||||
else:
|
patchelf_spec = Spec('patchelf').concretized()
|
||||||
if str(spack.architecture.platform()) == 'test':
|
patchelf = patchelf_spec.package
|
||||||
return None
|
if patchelf.installed:
|
||||||
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_executable = os.path.join(patchelf.prefix.bin, "patchelf")
|
patchelf_executable = os.path.join(patchelf.prefix.bin, "patchelf")
|
||||||
return patchelf_executable
|
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):
|
def get_existing_elf_rpaths(path_name):
|
||||||
@ -95,33 +109,53 @@ def get_existing_elf_rpaths(path_name):
|
|||||||
else:
|
else:
|
||||||
patchelf = Executable(get_patchelf())
|
patchelf = Executable(get_patchelf())
|
||||||
|
|
||||||
|
rpaths = list()
|
||||||
try:
|
try:
|
||||||
output = patchelf('--print-rpath', '%s' %
|
output = patchelf('--print-rpath', '%s' %
|
||||||
path_name, output=str, error=str)
|
path_name, output=str, error=str)
|
||||||
return output.rstrip('\n').split(':')
|
rpaths = output.rstrip('\n').split(':')
|
||||||
except ProcessError as e:
|
except ProcessError as e:
|
||||||
tty.debug('patchelf --print-rpath produced an error on %s' %
|
msg = 'patchelf --print-rpath %s produced an error %s' % (path_name, e)
|
||||||
path_name, e)
|
raise PatchelfError(msg)
|
||||||
return []
|
return rpaths
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
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
|
Replaces orig rpath with relative path from dirname(path_name) if an rpath
|
||||||
in orig_rpaths contains orig_path. Prefixes $ORIGIN
|
in orig_rpaths contains orig_layout_root. Prefixes $ORIGIN
|
||||||
to relative paths and returns replacement rpaths.
|
to relative paths and returns replacement rpaths.
|
||||||
"""
|
"""
|
||||||
rel_rpaths = []
|
rel_rpaths = []
|
||||||
for rpath in orig_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 = 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:
|
else:
|
||||||
rel_rpaths.append(rpath)
|
rel_rpaths.append(rpath)
|
||||||
return rel_rpaths
|
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):
|
def set_placeholder(dirname):
|
||||||
"""
|
"""
|
||||||
return string of @'s with same length
|
return string of @'s with same length
|
||||||
@ -129,183 +163,157 @@ def set_placeholder(dirname):
|
|||||||
return '@' * len(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 = []
|
paths_to_paths = dict()
|
||||||
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
|
|
||||||
if idpath:
|
if idpath:
|
||||||
new_idpath = '@rpath/%s' % os.path.basename(idpath)
|
paths_to_paths[idpath] = os.path.join(
|
||||||
new_rpaths = list()
|
'@rpath', '%s' % os.path.basename(idpath))
|
||||||
new_deps = list()
|
|
||||||
for rpath in rpaths:
|
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))
|
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:
|
else:
|
||||||
new_rpaths.append(rpath)
|
paths_to_paths[rpath] = rpath
|
||||||
for dep in deps:
|
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))
|
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:
|
else:
|
||||||
new_deps.append(dep)
|
paths_to_paths[dep] = dep
|
||||||
return (new_rpaths, new_deps, new_idpath)
|
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
|
Return a dictionary mapping the relativized rpaths to the original rpaths.
|
||||||
in rpaths and deps and idpaths is needed.
|
This dictionary is used to replace paths in mach-o binaries.
|
||||||
replacement are returned.
|
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
|
rel_to_orig = dict()
|
||||||
old_dir = spack.store.layout.root
|
|
||||||
placeholder = set_placeholder(old_dir)
|
|
||||||
if idpath:
|
if idpath:
|
||||||
new_idpath = re.sub(old_dir, placeholder, idpath)
|
rel_to_orig[idpath] = orig_path_name
|
||||||
new_rpaths = list()
|
|
||||||
new_deps = list()
|
|
||||||
for rpath in rpaths:
|
for rpath in rpaths:
|
||||||
if re.match(old_dir, rpath):
|
if re.match('@loader_path', rpath):
|
||||||
ph = re.sub(old_dir, placeholder, rpath)
|
norm = os.path.normpath(re.sub(re.escape('@loader_path'),
|
||||||
new_rpaths.append('%s' % ph)
|
os.path.dirname(orig_path_name),
|
||||||
|
rpath))
|
||||||
|
rel_to_orig[rpath] = norm
|
||||||
else:
|
else:
|
||||||
new_rpaths.append(rpath)
|
rel_to_orig[rpath] = rpath
|
||||||
for dep in deps:
|
for dep in deps:
|
||||||
if re.match(old_dir, dep):
|
if re.match('@loader_path', dep):
|
||||||
ph = re.sub(old_dir, placeholder, dep)
|
norm = os.path.normpath(re.sub(re.escape('@loader_path'),
|
||||||
new_deps.append('%s' % ph)
|
os.path.dirname(orig_path_name),
|
||||||
|
dep))
|
||||||
|
rel_to_orig[dep] = norm
|
||||||
else:
|
else:
|
||||||
new_deps.append(dep)
|
rel_to_orig[dep] = dep
|
||||||
return (new_rpaths, new_deps, new_idpath)
|
return rel_to_orig
|
||||||
|
|
||||||
|
|
||||||
def macho_replace_paths(old_dir, new_dir, rpaths, deps, idpath):
|
def macho_find_paths(orig_rpaths, deps, idpath,
|
||||||
|
old_layout_root, prefix_to_prefix):
|
||||||
"""
|
"""
|
||||||
Replace old_dir with new_dir in rpaths, deps and idpath
|
Inputs
|
||||||
and return replacements
|
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
|
||||||
"""
|
"""
|
||||||
new_idpath = None
|
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
|
||||||
|
|
||||||
if idpath:
|
if idpath:
|
||||||
new_idpath = idpath.replace(old_dir, new_dir)
|
for old_prefix, new_prefix in prefix_to_prefix.items():
|
||||||
new_rpaths = list()
|
if idpath.startswith(old_prefix):
|
||||||
new_deps = list()
|
paths_to_paths[idpath] = re.sub(
|
||||||
for rpath in rpaths:
|
re.escape(old_prefix), new_prefix, idpath)
|
||||||
new_rpath = rpath.replace(old_dir, new_dir)
|
|
||||||
new_rpaths.append(new_rpath)
|
|
||||||
for dep in deps:
|
for dep in deps:
|
||||||
new_dep = dep.replace(old_dir, new_dir)
|
for old_prefix, new_prefix in prefix_to_prefix.items():
|
||||||
new_deps.append(new_dep)
|
if dep.startswith(old_prefix):
|
||||||
return new_rpaths, new_deps, new_idpath
|
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,
|
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
|
This function is used to make machO buildcaches on macOS by
|
||||||
or the relative path to spack install root.
|
replacing old paths with new paths using install_name_tool
|
||||||
The old install dir in LC_ID_DYLIB is replaced with the new install dir
|
Inputs:
|
||||||
using install_name_tool -id newid binary
|
mach-o binary to be modified
|
||||||
The old install dir in LC_LOAD_DYLIB is replaced with the new install dir
|
original rpaths
|
||||||
using install_name_tool -change old new binary
|
original dependency paths
|
||||||
The old install dir in LC_RPATH is replaced with the new install dir using
|
original id path if a mach-o library
|
||||||
install_name_tool -rpath old new binary
|
dictionary mapping paths in old install layout to new install layout
|
||||||
"""
|
"""
|
||||||
# avoid error message for libgcc_s
|
# avoid error message for libgcc_s
|
||||||
if 'libgcc_' in cur_path:
|
if 'libgcc_' in cur_path:
|
||||||
return
|
return
|
||||||
install_name_tool = Executable('install_name_tool')
|
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):
|
if idpath:
|
||||||
for orig, new in zip(deps, new_deps):
|
new_idpath = paths_to_paths.get(idpath, None)
|
||||||
if not orig == new:
|
if new_idpath and not idpath == new_idpath:
|
||||||
install_name_tool('-change', orig, new, str(cur_path))
|
install_name_tool('-id', new_idpath, str(cur_path))
|
||||||
|
for dep in deps:
|
||||||
if len(rpaths) == len(new_rpaths):
|
new_dep = paths_to_paths.get(dep)
|
||||||
for orig, new in zip(rpaths, new_rpaths):
|
if new_dep and dep != new_dep:
|
||||||
if not orig == new:
|
install_name_tool('-change', dep, new_dep, str(cur_path))
|
||||||
install_name_tool('-rpath', orig, new, 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
|
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
|
This function is used when install machO buildcaches on linux by
|
||||||
or the relative path to spack install root.
|
rewriting mach-o loader commands for dependency library paths of
|
||||||
The old install dir in LC_ID_DYLIB is replaced with the new install dir
|
mach-o binaries and the id path for mach-o libraries.
|
||||||
using py-macholib
|
Rewritting of rpaths is handled by replace_prefix_bin.
|
||||||
The old install dir in LC_LOAD_DYLIB is replaced with the new install dir
|
Inputs
|
||||||
using py-macholib
|
mach-o binary to be modified
|
||||||
The old install dir in LC_RPATH is replaced with the new install dir using
|
dictionary mapping paths in old install layout to new install layout
|
||||||
using py-macholib
|
|
||||||
"""
|
"""
|
||||||
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 = 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:
|
try:
|
||||||
f = open(dll.filename, 'rb+')
|
f = open(dll.filename, 'rb+')
|
||||||
for header in dll.headers:
|
for header in dll.headers:
|
||||||
@ -320,14 +328,32 @@ def match_func(cpath):
|
|||||||
return
|
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')
|
dll = MachO(cur_path)
|
||||||
output = strings('%s' % path_name,
|
|
||||||
output=str, err=str)
|
ident = None
|
||||||
return (root_dir in output or spack.paths.prefix in output)
|
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):
|
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)
|
new_joined = ':'.join(new_rpaths)
|
||||||
|
|
||||||
# if we're relocating patchelf itself, use it
|
# if we're relocating patchelf itself, use it
|
||||||
|
bak_path = path_name + ".bak"
|
||||||
|
|
||||||
if path_name[-13:] == "/bin/patchelf":
|
if path_name[-13:] == "/bin/patchelf":
|
||||||
bak_path = path_name + ".bak"
|
|
||||||
shutil.copy(path_name, bak_path)
|
shutil.copy(path_name, bak_path)
|
||||||
patchelf = Executable(bak_path)
|
patchelf = Executable(bak_path)
|
||||||
else:
|
else:
|
||||||
@ -350,9 +376,11 @@ def modify_elf_object(path_name, new_rpaths):
|
|||||||
patchelf('--force-rpath', '--set-rpath', '%s' % new_joined,
|
patchelf('--force-rpath', '--set-rpath', '%s' % new_joined,
|
||||||
'%s' % path_name, output=str, error=str)
|
'%s' % path_name, output=str, error=str)
|
||||||
except ProcessError as e:
|
except ProcessError as e:
|
||||||
tty.die('patchelf --set-rpath %s failed' %
|
msg = 'patchelf --set-rpath %s failed with error %s' % (path_name, e)
|
||||||
path_name, e)
|
raise PatchelfError(msg)
|
||||||
pass
|
pass
|
||||||
|
if os.path.exists(bak_path):
|
||||||
|
os.remove(bak_path)
|
||||||
|
|
||||||
|
|
||||||
def needs_binary_relocation(m_type, m_subtype):
|
def needs_binary_relocation(m_type, m_subtype):
|
||||||
@ -447,11 +475,15 @@ def replace(match):
|
|||||||
return data
|
return data
|
||||||
return match.group().replace(old_dir.encode('utf-8'),
|
return match.group().replace(old_dir.encode('utf-8'),
|
||||||
new_dir.encode('utf-8')) + b'\0' * padding
|
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:
|
with open(path_name, 'rb+') as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
original_data_len = len(data)
|
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):
|
if not pat.search(data):
|
||||||
return
|
return
|
||||||
ndata = pat.sub(replace, data)
|
ndata = pat.sub(replace, data)
|
||||||
@ -462,80 +494,129 @@ def replace(match):
|
|||||||
f.truncate()
|
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)
|
Use macholib python package to get the rpaths, depedent libraries
|
||||||
Change old_dir to new_dir in LC_ID and LC_DEP of mach-o files
|
and library identity for libraries from the MachO object. Modify them
|
||||||
Account for the case where old_dir is now a placeholder
|
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:
|
for path_name in path_names:
|
||||||
|
# Corner case where macho object file ended up in the path name list
|
||||||
if path_name.endswith('.o'):
|
if path_name.endswith('.o'):
|
||||||
continue
|
continue
|
||||||
if new_dir == old_dir:
|
if rel:
|
||||||
continue
|
# get the relativized paths
|
||||||
if platform.system().lower() == 'darwin':
|
rpaths, deps, idpath = macholib_get_paths(path_name)
|
||||||
rpaths, deps, idpath = macho_get_paths(path_name)
|
# get the file path name in the original prefix
|
||||||
# one pass to replace placeholder
|
orig_path_name = re.sub(re.escape(new_prefix), old_prefix,
|
||||||
(n_rpaths,
|
path_name)
|
||||||
n_deps,
|
# get the mapping of the relativized paths to the original
|
||||||
n_idpath) = macho_replace_paths(placeholder,
|
# normalized paths
|
||||||
new_dir,
|
rel_to_orig = macho_make_paths_normal(orig_path_name,
|
||||||
rpaths,
|
rpaths, deps,
|
||||||
deps,
|
idpath)
|
||||||
idpath)
|
# replace the relativized paths with normalized paths
|
||||||
# another pass to replace old_dir
|
if platform.system().lower() == 'darwin':
|
||||||
(new_rpaths,
|
modify_macho_object(path_name, rpaths, deps,
|
||||||
new_deps,
|
idpath, rel_to_orig)
|
||||||
new_idpath) = macho_replace_paths(old_dir,
|
else:
|
||||||
new_dir,
|
modify_object_macholib(path_name,
|
||||||
n_rpaths,
|
rel_to_orig)
|
||||||
n_deps,
|
# get the normalized paths in the mach-o binary
|
||||||
n_idpath)
|
rpaths, deps, idpath = macholib_get_paths(path_name)
|
||||||
modify_macho_object(path_name,
|
# get the mapping of paths in old prefix to path in new prefix
|
||||||
rpaths, deps, idpath,
|
paths_to_paths = macho_find_paths(rpaths, deps, idpath,
|
||||||
new_rpaths, new_deps, new_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:
|
else:
|
||||||
modify_object_macholib(path_name, placeholder, new_dir)
|
# get the paths in the old prefix
|
||||||
modify_object_macholib(path_name, old_dir, new_dir)
|
rpaths, deps, idpath = macholib_get_paths(path_name)
|
||||||
if len(new_dir) <= len(old_dir):
|
# get the mapping of paths in the old prerix to the new prefix
|
||||||
replace_prefix_nullterm(path_name, old_dir, new_dir)
|
paths_to_paths = macho_find_paths(rpaths, deps, idpath,
|
||||||
else:
|
old_layout_root,
|
||||||
tty.warn('Cannot do a binary string replacement'
|
prefix_to_prefix)
|
||||||
' with padding for %s'
|
# replace the old paths with new paths
|
||||||
' because %s is longer than %s' %
|
if platform.system().lower() == 'darwin':
|
||||||
(path_name, new_dir, old_dir))
|
modify_macho_object(path_name, rpaths, deps,
|
||||||
|
idpath, paths_to_paths)
|
||||||
|
else:
|
||||||
|
modify_object_macholib(path_name,
|
||||||
|
paths_to_paths)
|
||||||
|
|
||||||
|
|
||||||
def relocate_elf_binaries(path_names, old_dir, new_dir, allow_root):
|
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:
|
||||||
|
new_rpaths.append(orig_rpath)
|
||||||
|
return new_rpaths
|
||||||
|
|
||||||
|
|
||||||
|
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
|
Use patchelf to get the original rpaths and then replace them with
|
||||||
Account for the case where old_dir is now a placeholder
|
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:
|
for path_name in path_names:
|
||||||
orig_rpaths = get_existing_elf_rpaths(path_name)
|
orig_rpaths = get_existing_elf_rpaths(path_name)
|
||||||
if orig_rpaths:
|
new_rpaths = list()
|
||||||
# one pass to replace placeholder
|
if rel:
|
||||||
n_rpaths = substitute_rpath(orig_rpaths,
|
# get the file path in the old_prefix
|
||||||
placeholder, new_dir)
|
orig_path_name = re.sub(re.escape(new_prefix), old_prefix,
|
||||||
# one pass to replace old_dir
|
path_name)
|
||||||
new_rpaths = substitute_rpath(n_rpaths,
|
# get the normalized rpaths in the old prefix using the file path
|
||||||
old_dir, new_dir)
|
# 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)
|
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):
|
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):
|
for cur_path, orig_path in zip(cur_path_names, orig_path_names):
|
||||||
target = os.readlink(orig_path)
|
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)
|
os.symlink(relative_target, cur_path)
|
||||||
|
|
||||||
|
|
||||||
def make_macho_binaries_relative(cur_path_names, orig_path_names, old_dir,
|
def make_macho_binaries_relative(cur_path_names, orig_path_names,
|
||||||
allow_root):
|
old_layout_root):
|
||||||
"""
|
"""
|
||||||
Replace old RPATHs with paths relative to old_dir in binary files
|
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()
|
deps = set()
|
||||||
idpath = None
|
idpath = None
|
||||||
if platform.system().lower() == 'darwin':
|
if platform.system().lower() == 'darwin':
|
||||||
(rpaths, deps, idpath) = macho_get_paths(cur_path)
|
(rpaths, deps, idpath) = macholib_get_paths(cur_path)
|
||||||
(new_rpaths,
|
paths_to_paths = macho_make_paths_relative(orig_path,
|
||||||
new_deps,
|
old_layout_root,
|
||||||
new_idpath) = macho_make_paths_relative(orig_path, old_dir,
|
rpaths, deps, idpath)
|
||||||
rpaths, deps, idpath)
|
|
||||||
modify_macho_object(cur_path,
|
modify_macho_object(cur_path,
|
||||||
rpaths, deps, idpath,
|
rpaths, deps, idpath,
|
||||||
new_rpaths, new_deps, new_idpath)
|
paths_to_paths)
|
||||||
if (not allow_root and
|
|
||||||
not file_is_relocatable(cur_path)):
|
|
||||||
raise InstallRootStringException(cur_path, old_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def make_elf_binaries_relative(cur_path_names, orig_path_names, old_dir,
|
def make_elf_binaries_relative(cur_path_names, orig_path_names,
|
||||||
allow_root):
|
old_layout_root):
|
||||||
"""
|
"""
|
||||||
Replace old RPATHs with paths relative to old_dir in binary files
|
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):
|
for cur_path, orig_path in zip(cur_path_names, orig_path_names):
|
||||||
orig_rpaths = get_existing_elf_rpaths(cur_path)
|
orig_rpaths = get_existing_elf_rpaths(cur_path)
|
||||||
if orig_rpaths:
|
if orig_rpaths:
|
||||||
new_rpaths = get_relative_rpaths(orig_path, old_dir,
|
new_rpaths = get_relative_elf_rpaths(orig_path, old_layout_root,
|
||||||
orig_rpaths)
|
orig_rpaths)
|
||||||
modify_elf_object(cur_path, new_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):
|
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)
|
cur_path, spack.store.layout.root)
|
||||||
|
|
||||||
|
|
||||||
def make_link_placeholder(cur_path_names, cur_dir, old_dir):
|
def relocate_links(linknames, old_layout_root, new_layout_root,
|
||||||
|
old_install_prefix, new_install_prefix, prefix_to_prefix):
|
||||||
"""
|
"""
|
||||||
Replace old install path with placeholder in absolute links.
|
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
|
||||||
Links in ``cur_path_names`` must link to absolute paths.
|
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.
|
||||||
"""
|
"""
|
||||||
for cur_path in cur_path_names:
|
placeholder = set_placeholder(old_layout_root)
|
||||||
placeholder = set_placeholder(spack.store.layout.root)
|
link_names = [os.path.join(new_install_prefix, linkname)
|
||||||
placeholder_prefix = old_dir.replace(spack.store.layout.root,
|
for linkname in linknames]
|
||||||
placeholder)
|
for link_name in link_names:
|
||||||
cur_src = os.readlink(cur_path)
|
old_link_target = os.readlink(link_name)
|
||||||
rel_src = os.path.relpath(cur_src, cur_dir)
|
old_link_target = re.sub(placeholder, old_layout_root, old_link_target)
|
||||||
new_src = os.path.join(placeholder_prefix, rel_src)
|
if old_link_target.startswith(old_install_prefix):
|
||||||
|
new_link_target = re.sub(
|
||||||
os.unlink(cur_path)
|
old_install_prefix, new_install_prefix, old_link_target)
|
||||||
os.symlink(new_src, cur_path)
|
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.
|
Replace old paths with new paths in text files
|
||||||
|
including the path the the spack sbang script
|
||||||
Links in ``path_names`` must link to absolute paths or placeholders.
|
|
||||||
"""
|
"""
|
||||||
placeholder = set_placeholder(old_dir)
|
sbangre = '#!/bin/bash %s/bin/sbang' % old_spack_prefix
|
||||||
|
sbangnew = '#!/bin/bash %s/bin/sbang' % new_spack_prefix
|
||||||
|
|
||||||
for path_name in path_names:
|
for path_name in path_names:
|
||||||
old_src = os.readlink(path_name)
|
replace_prefix_text(path_name, old_install_prefix, new_install_prefix)
|
||||||
# replace either placeholder or old_dir
|
for orig_dep_prefix, new_dep_prefix in prefix_to_prefix.items():
|
||||||
new_src = old_src.replace(placeholder, new_dir, 1)
|
replace_prefix_text(path_name, orig_dep_prefix, new_dep_prefix)
|
||||||
new_src = new_src.replace(old_dir, new_dir, 1)
|
replace_prefix_text(path_name, old_layout_root, new_layout_root)
|
||||||
|
|
||||||
os.unlink(path_name)
|
|
||||||
os.symlink(new_src, path_name)
|
|
||||||
|
|
||||||
|
|
||||||
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, sbangre, sbangnew)
|
replace_prefix_text(path_name, sbangre, sbangnew)
|
||||||
replace_prefix_text(path_name, oldprefix, newprefix)
|
|
||||||
|
|
||||||
|
|
||||||
def substitute_rpath(orig_rpath, topdir, new_root_path):
|
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 topdir with new_root_path RPATH list orig_rpath
|
Replace null terminated path strings hard coded into binaries.
|
||||||
"""
|
Raise an exception when the new path in longer than the old path
|
||||||
new_rpaths = []
|
because this breaks the binary.
|
||||||
for path in orig_rpath:
|
"""
|
||||||
new_rpath = path.replace(topdir, new_root_path)
|
if len(new_install_prefix) <= len(old_install_prefix):
|
||||||
new_rpaths.append(new_rpath)
|
for path_name in path_names:
|
||||||
return new_rpaths
|
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):
|
def is_relocatable(spec):
|
||||||
@ -729,7 +814,7 @@ def file_is_relocatable(file, paths_to_relocate=None):
|
|||||||
set_of_strings.discard(rpaths)
|
set_of_strings.discard(rpaths)
|
||||||
if platform.system().lower() == 'darwin':
|
if platform.system().lower() == 'darwin':
|
||||||
if m_subtype == 'x-mach-binary':
|
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(rpaths))
|
||||||
set_of_strings.discard(set(deps))
|
set_of_strings.discard(set(deps))
|
||||||
if idpath is not None:
|
if idpath is not None:
|
||||||
@ -779,6 +864,8 @@ def mime_type(file):
|
|||||||
file_cmd = Executable('file')
|
file_cmd = Executable('file')
|
||||||
output = file_cmd('-b', '-h', '--mime-type', file, output=str, error=str)
|
output = file_cmd('-b', '-h', '--mime-type', file, output=str, error=str)
|
||||||
tty.debug('[MIME_TYPE] {0} -> {1}'.format(file, output.strip()))
|
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:
|
if '/' not in output:
|
||||||
output += '/'
|
output += '/'
|
||||||
split_by_slash = output.strip().split('/')
|
split_by_slash = output.strip().split('/')
|
||||||
|
@ -8,10 +8,11 @@
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import sys
|
|
||||||
import shutil
|
import shutil
|
||||||
import pytest
|
import pytest
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
|
import platform
|
||||||
|
|
||||||
from llnl.util.filesystem import mkdirp
|
from llnl.util.filesystem import mkdirp
|
||||||
|
|
||||||
@ -19,16 +20,15 @@
|
|||||||
import spack.store
|
import spack.store
|
||||||
import spack.binary_distribution as bindist
|
import spack.binary_distribution as bindist
|
||||||
import spack.cmd.buildcache as buildcache
|
import spack.cmd.buildcache as buildcache
|
||||||
import spack.util.gpg
|
|
||||||
from spack.spec import Spec
|
from spack.spec import Spec
|
||||||
from spack.paths import mock_gpg_keys_path
|
from spack.paths import mock_gpg_keys_path
|
||||||
from spack.fetch_strategy import URLFetchStrategy, FetchStrategyComposite
|
from spack.fetch_strategy import URLFetchStrategy, FetchStrategyComposite
|
||||||
from spack.relocate import needs_binary_relocation, needs_text_relocation
|
from spack.relocate import needs_binary_relocation, needs_text_relocation
|
||||||
from spack.relocate import strings_contains_installroot
|
from spack.relocate import relocate_text, relocate_links
|
||||||
from spack.relocate import get_patchelf, relocate_text, relocate_links
|
from spack.relocate import get_relative_elf_rpaths
|
||||||
from spack.relocate import substitute_rpath, get_relative_rpaths
|
from spack.relocate import macho_make_paths_relative
|
||||||
from spack.relocate import macho_replace_paths, macho_make_paths_relative
|
from spack.relocate import set_placeholder, macho_find_paths
|
||||||
from spack.relocate import modify_macho_object, macho_get_paths
|
from spack.relocate import file_is_relocatable
|
||||||
|
|
||||||
|
|
||||||
def has_gpg():
|
def has_gpg():
|
||||||
@ -50,9 +50,9 @@ def fake_fetchify(url, pkg):
|
|||||||
@pytest.mark.usefixtures('install_mockery', 'mock_gnupghome')
|
@pytest.mark.usefixtures('install_mockery', 'mock_gnupghome')
|
||||||
def test_buildcache(mock_archive, tmpdir):
|
def test_buildcache(mock_archive, tmpdir):
|
||||||
# tweak patchelf to only do a download
|
# tweak patchelf to only do a download
|
||||||
spec = Spec("patchelf")
|
pspec = Spec("patchelf")
|
||||||
spec.concretize()
|
pspec.concretize()
|
||||||
pkg = spack.repo.get(spec)
|
pkg = spack.repo.get(pspec)
|
||||||
fake_fetchify(pkg.fetcher, pkg)
|
fake_fetchify(pkg.fetcher, pkg)
|
||||||
mkdirp(os.path.join(pkg.prefix, "bin"))
|
mkdirp(os.path.join(pkg.prefix, "bin"))
|
||||||
patchelfscr = os.path.join(pkg.prefix, "bin", "patchelf")
|
patchelfscr = os.path.join(pkg.prefix, "bin", "patchelf")
|
||||||
@ -71,7 +71,7 @@ def test_buildcache(mock_archive, tmpdir):
|
|||||||
pkg = spec.package
|
pkg = spec.package
|
||||||
fake_fetchify(mock_archive.url, pkg)
|
fake_fetchify(mock_archive.url, pkg)
|
||||||
pkg.do_install()
|
pkg.do_install()
|
||||||
pkghash = '/' + spec.dag_hash(7)
|
pkghash = '/' + str(spec.dag_hash(7))
|
||||||
|
|
||||||
# Put some non-relocatable file in there
|
# Put some non-relocatable file in there
|
||||||
filename = os.path.join(spec.prefix, "dummy.txt")
|
filename = os.path.join(spec.prefix, "dummy.txt")
|
||||||
@ -99,88 +99,69 @@ def test_buildcache(mock_archive, tmpdir):
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
buildcache.setup_parser(parser)
|
buildcache.setup_parser(parser)
|
||||||
|
|
||||||
|
create_args = ['create', '-a', '-f', '-d', mirror_path, pkghash]
|
||||||
# Create a private key to sign package with if gpg2 available
|
# Create a private key to sign package with if gpg2 available
|
||||||
if spack.util.gpg.Gpg.gpg():
|
if spack.util.gpg.Gpg.gpg():
|
||||||
spack.util.gpg.Gpg.create(name='test key 1', expires='0',
|
spack.util.gpg.Gpg.create(name='test key 1', expires='0',
|
||||||
email='spack@googlegroups.com',
|
email='spack@googlegroups.com',
|
||||||
comment='Spack test key')
|
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)
|
|
||||||
|
|
||||||
# create build cache with relative path and signing
|
|
||||||
args = parser.parse_args(
|
|
||||||
['create', '-d', mirror_path, '-f', '-r', str(spec)])
|
|
||||||
buildcache.buildcache(parser, args)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# test overwrite install
|
|
||||||
args = parser.parse_args(['install', '-f', str(pkghash)])
|
|
||||||
buildcache.buildcache(parser, args)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# create build cache without signing
|
create_args.insert(create_args.index('-a'), '-u')
|
||||||
args = parser.parse_args(
|
|
||||||
['create', '-d', mirror_path, '-f', '-u', str(spec)])
|
|
||||||
buildcache.buildcache(parser, args)
|
|
||||||
|
|
||||||
# Uninstall the package
|
args = parser.parse_args(create_args)
|
||||||
pkg.do_uninstall(force=True)
|
buildcache.buildcache(parser, args)
|
||||||
|
# trigger overwrite warning
|
||||||
|
buildcache.buildcache(parser, args)
|
||||||
|
|
||||||
# install build cache without verification
|
# Uninstall the package
|
||||||
args = parser.parse_args(['install', '-u', str(spec)])
|
pkg.do_uninstall(force=True)
|
||||||
buildcache.install_tarball(spec, args)
|
|
||||||
|
|
||||||
files = os.listdir(spec.prefix)
|
install_args = ['install', '-a', '-f', pkghash]
|
||||||
assert 'link_to_dummy.txt' in files
|
if not spack.util.gpg.Gpg.gpg():
|
||||||
assert 'dummy.txt' in files
|
install_args.insert(install_args.index('-a'), '-u')
|
||||||
# test overwrite install without verification
|
args = parser.parse_args(install_args)
|
||||||
args = parser.parse_args(['install', '-f', '-u', str(pkghash)])
|
# Test install
|
||||||
buildcache.buildcache(parser, args)
|
buildcache.buildcache(parser, args)
|
||||||
|
|
||||||
# create build cache with relative path
|
files = os.listdir(spec.prefix)
|
||||||
args = parser.parse_args(
|
|
||||||
['create', '-d', mirror_path, '-f', '-r', '-u', str(pkghash)])
|
|
||||||
buildcache.buildcache(parser, args)
|
|
||||||
|
|
||||||
# Uninstall the package
|
assert 'link_to_dummy.txt' in files
|
||||||
pkg.do_uninstall(force=True)
|
assert 'dummy.txt' in files
|
||||||
|
|
||||||
# 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'))
|
|
||||||
|
|
||||||
# Validate the relocation information
|
# Validate the relocation information
|
||||||
buildinfo = bindist.read_buildinfo_file(spec.prefix)
|
buildinfo = bindist.read_buildinfo_file(spec.prefix)
|
||||||
assert(buildinfo['relocate_textfiles'] == ['dummy.txt'])
|
assert(buildinfo['relocate_textfiles'] == ['dummy.txt'])
|
||||||
assert(buildinfo['relocate_links'] == ['link_to_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'])
|
args = parser.parse_args(['list'])
|
||||||
buildcache.buildcache(parser, args)
|
buildcache.buildcache(parser, args)
|
||||||
|
|
||||||
@ -200,6 +181,9 @@ def test_buildcache(mock_archive, tmpdir):
|
|||||||
args = parser.parse_args(['keys', '-f'])
|
args = parser.parse_args(['keys', '-f'])
|
||||||
buildcache.buildcache(parser, args)
|
buildcache.buildcache(parser, args)
|
||||||
|
|
||||||
|
args = parser.parse_args(['keys', '-i', '-t'])
|
||||||
|
buildcache.buildcache(parser, args)
|
||||||
|
|
||||||
# unregister mirror with spack config
|
# unregister mirror with spack config
|
||||||
mirrors = {}
|
mirrors = {}
|
||||||
spack.config.set('mirrors', mirrors)
|
spack.config.set('mirrors', mirrors)
|
||||||
@ -210,7 +194,10 @@ def test_buildcache(mock_archive, tmpdir):
|
|||||||
bindist._cached_specs = set()
|
bindist._cached_specs = set()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('install_mockery')
|
||||||
def test_relocate_text(tmpdir):
|
def test_relocate_text(tmpdir):
|
||||||
|
spec = Spec('trivial-install-test-package')
|
||||||
|
spec.concretize()
|
||||||
with tmpdir.as_cwd():
|
with tmpdir.as_cwd():
|
||||||
# Validate the text path replacement
|
# Validate the text path replacement
|
||||||
old_dir = '/home/spack/opt/spack'
|
old_dir = '/home/spack/opt/spack'
|
||||||
@ -220,24 +207,46 @@ def test_relocate_text(tmpdir):
|
|||||||
script.close()
|
script.close()
|
||||||
filenames = [filename]
|
filenames = [filename]
|
||||||
new_dir = '/opt/rh/devtoolset/'
|
new_dir = '/opt/rh/devtoolset/'
|
||||||
relocate_text(filenames, oldpath=old_dir, newpath=new_dir,
|
relocate_text(filenames, old_dir, new_dir,
|
||||||
oldprefix=old_dir, newprefix=new_dir)
|
old_dir, new_dir,
|
||||||
|
old_dir, new_dir,
|
||||||
|
{old_dir: new_dir})
|
||||||
with open(filename, "r")as script:
|
with open(filename, "r")as script:
|
||||||
for line in script:
|
for line in script:
|
||||||
assert(new_dir in line)
|
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):
|
def test_relocate_links(tmpdir):
|
||||||
with tmpdir.as_cwd():
|
with tmpdir.as_cwd():
|
||||||
old_dir = '/home/spack/opt/spack'
|
old_layout_root = os.path.join(
|
||||||
filename = 'link.ln'
|
'%s' % tmpdir, 'home', 'spack', 'opt', 'spack')
|
||||||
old_src = os.path.join(old_dir, filename)
|
old_install_prefix = os.path.join(
|
||||||
os.symlink(old_src, filename)
|
'%s' % old_layout_root, 'debian6', 'test')
|
||||||
filenames = [filename]
|
old_binname = os.path.join(old_install_prefix, 'binfile')
|
||||||
new_dir = '/opt/rh/devtoolset'
|
placeholder = set_placeholder(old_layout_root)
|
||||||
relocate_links(filenames, old_dir, new_dir)
|
re.sub(old_layout_root, placeholder, old_binname)
|
||||||
assert os.path.realpath(filename) == os.path.join(new_dir, filename)
|
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():
|
def test_needs_relocation():
|
||||||
@ -246,15 +255,222 @@ def test_needs_relocation():
|
|||||||
assert needs_binary_relocation('application', 'x-executable')
|
assert needs_binary_relocation('application', 'x-executable')
|
||||||
assert not needs_binary_relocation('application', 'x-octet-stream')
|
assert not needs_binary_relocation('application', 'x-octet-stream')
|
||||||
assert not needs_binary_relocation('text', 'x-')
|
assert not needs_binary_relocation('text', 'x-')
|
||||||
|
|
||||||
assert needs_text_relocation('text', 'x-')
|
assert needs_text_relocation('text', 'x-')
|
||||||
assert not needs_text_relocation('symbolic link to', 'x-')
|
assert not needs_text_relocation('symbolic link to', 'x-')
|
||||||
|
|
||||||
assert needs_binary_relocation('application', 'x-mach-binary')
|
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',
|
out = macho_make_paths_relative('/Users/Shares/spack/pkgC/lib/libC.dylib',
|
||||||
'/Users/Shared/spack',
|
'/Users/Shared/spack',
|
||||||
('/Users/Shared/spack/pkgA/lib',
|
('/Users/Shared/spack/pkgA/lib',
|
||||||
@ -264,13 +480,19 @@ def test_macho_paths():
|
|||||||
'/Users/Shared/spack/pkgB/libB.dylib',
|
'/Users/Shared/spack/pkgB/libB.dylib',
|
||||||
'/usr/local/lib/libloco.dylib'),
|
'/usr/local/lib/libloco.dylib'),
|
||||||
'/Users/Shared/spack/pkgC/lib/libC.dylib')
|
'/Users/Shared/spack/pkgC/lib/libC.dylib')
|
||||||
assert out == (['@loader_path/../../../../Shared/spack/pkgA/lib',
|
assert out == {'/Users/Shared/spack/pkgA/lib':
|
||||||
'@loader_path/../../../../Shared/spack/pkgB/lib',
|
'@loader_path/../../../../Shared/spack/pkgA/lib',
|
||||||
'/usr/local/lib'],
|
'/Users/Shared/spack/pkgB/lib':
|
||||||
['@loader_path/../../../../Shared/spack/pkgA/libA.dylib',
|
'@loader_path/../../../../Shared/spack/pkgB/lib',
|
||||||
'@loader_path/../../../../Shared/spack/pkgB/libB.dylib',
|
'/usr/local/lib': '/usr/local/lib',
|
||||||
'/usr/local/lib/libloco.dylib'],
|
'/Users/Shared/spack/pkgA/libA.dylib':
|
||||||
'@rpath/libC.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',
|
out = macho_make_paths_relative('/Users/Shared/spack/pkgC/bin/exeC',
|
||||||
'/Users/Shared/spack',
|
'/Users/Shared/spack',
|
||||||
@ -281,98 +503,21 @@ def test_macho_paths():
|
|||||||
'/Users/Shared/spack/pkgB/libB.dylib',
|
'/Users/Shared/spack/pkgB/libB.dylib',
|
||||||
'/usr/local/lib/libloco.dylib'), None)
|
'/usr/local/lib/libloco.dylib'), None)
|
||||||
|
|
||||||
assert out == (['@loader_path/../../pkgA/lib',
|
assert out == {'/Users/Shared/spack/pkgA/lib':
|
||||||
'@loader_path/../../pkgB/lib',
|
'@loader_path/../../pkgA/lib',
|
||||||
'/usr/local/lib'],
|
'/Users/Shared/spack/pkgB/lib':
|
||||||
['@loader_path/../../pkgA/libA.dylib',
|
'@loader_path/../../pkgB/lib',
|
||||||
'@loader_path/../../pkgB/libB.dylib',
|
'/usr/local/lib': '/usr/local/lib',
|
||||||
'/usr/local/lib/libloco.dylib'], None)
|
'/Users/Shared/spack/pkgA/libA.dylib':
|
||||||
|
'@loader_path/../../pkgA/libA.dylib',
|
||||||
out = macho_replace_paths('/Users/Shared/spack',
|
'/Users/Shared/spack/pkgB/libB.dylib':
|
||||||
'/Applications/spack',
|
'@loader_path/../../pkgB/libB.dylib',
|
||||||
('/Users/Shared/spack/pkgA/lib',
|
'/usr/local/lib/libloco.dylib':
|
||||||
'/Users/Shared/spack/pkgB/lib',
|
'/usr/local/lib/libloco.dylib'}
|
||||||
'/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)
|
|
||||||
|
|
||||||
|
|
||||||
def test_elf_paths():
|
def test_elf_paths():
|
||||||
out = get_relative_rpaths(
|
out = get_relative_elf_rpaths(
|
||||||
'/usr/bin/test', '/usr',
|
'/usr/bin/test', '/usr',
|
||||||
('/usr/lib', '/usr/lib64', '/opt/local/lib'))
|
('/usr/lib', '/usr/lib64', '/opt/local/lib'))
|
||||||
assert out == ['$ORIGIN/../lib', '$ORIGIN/../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)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user