Windows: Symlink support
To provide Windows-compatible functionality, spack code should use llnl.util.symlink instead of os.symlink. On non-Windows platforms and on Windows where supported, os.symlink will still be used. Use junctions when symlinks aren't supported on Windows (#22583) Support islink for junctions (#24182) Windows: Update llnl/util/filesystem * Use '/' as path separator on Windows. * Recognizing that Windows paths start with '<Letter>:/' instead of '/' Co-authored-by: lou.lawrence@kitware.com <lou.lawrence@kitware.com> Co-authored-by: John Parent <john.parent@kitware.com>
This commit is contained in:
parent
a7de2fa380
commit
fb0e91c534
20 changed files with 224 additions and 44 deletions
4
lib/spack/external/macholib/util.py
vendored
4
lib/spack/external/macholib/util.py
vendored
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
from macholib import mach_o
|
from macholib import mach_o
|
||||||
|
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
MAGIC = [
|
MAGIC = [
|
||||||
struct.pack("!L", getattr(mach_o, "MH_" + _))
|
struct.pack("!L", getattr(mach_o, "MH_" + _))
|
||||||
for _ in ["MAGIC", "CIGAM", "MAGIC_64", "CIGAM_64"]
|
for _ in ["MAGIC", "CIGAM", "MAGIC_64", "CIGAM_64"]
|
||||||
|
@ -140,7 +142,7 @@ def mergetree(src, dst, condition=None, copyfn=mergecopy, srcbase=None):
|
||||||
try:
|
try:
|
||||||
if os.path.islink(srcname):
|
if os.path.islink(srcname):
|
||||||
realsrc = os.readlink(srcname)
|
realsrc = os.readlink(srcname)
|
||||||
os.symlink(realsrc, dstname)
|
symlink(realsrc, dstname)
|
||||||
elif os.path.isdir(srcname):
|
elif os.path.isdir(srcname):
|
||||||
mergetree(
|
mergetree(
|
||||||
srcname,
|
srcname,
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
from os.path import abspath, normpath, isabs, exists, isdir, isfile, islink, dirname
|
from os.path import abspath, normpath, isabs, exists, isdir, isfile, islink, dirname
|
||||||
|
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
if sys.version_info > (3,0):
|
if sys.version_info > (3,0):
|
||||||
def map_as_list(func, iter):
|
def map_as_list(func, iter):
|
||||||
return list(map(func, iter))
|
return list(map(func, iter))
|
||||||
|
@ -79,7 +81,7 @@ def mklinkto(self, oldname):
|
||||||
def mksymlinkto(self, value, absolute=1):
|
def mksymlinkto(self, value, absolute=1):
|
||||||
""" create a symbolic link with the given value (pointing to another name). """
|
""" create a symbolic link with the given value (pointing to another name). """
|
||||||
if absolute:
|
if absolute:
|
||||||
py.error.checked_call(os.symlink, str(value), self.strpath)
|
py.error.checked_call(symlink, str(value), self.strpath)
|
||||||
else:
|
else:
|
||||||
base = self.common(value)
|
base = self.common(value)
|
||||||
# with posix local paths '/' is always a common base
|
# with posix local paths '/' is always a common base
|
||||||
|
@ -87,7 +89,7 @@ def mksymlinkto(self, value, absolute=1):
|
||||||
reldest = self.relto(base)
|
reldest = self.relto(base)
|
||||||
n = reldest.count(self.sep)
|
n = reldest.count(self.sep)
|
||||||
target = self.sep.join(('..', )*n + (relsource, ))
|
target = self.sep.join(('..', )*n + (relsource, ))
|
||||||
py.error.checked_call(os.symlink, target, self.strpath)
|
py.error.checked_call(symlink, target, self.strpath)
|
||||||
|
|
||||||
def getuserid(user):
|
def getuserid(user):
|
||||||
import pwd
|
import pwd
|
||||||
|
@ -892,7 +894,7 @@ def try_remove_lockfile():
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
os.symlink(src, dest)
|
symlink(src, dest)
|
||||||
except (OSError, AttributeError, NotImplementedError):
|
except (OSError, AttributeError, NotImplementedError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
from llnl.util import tty
|
from llnl.util import tty
|
||||||
from llnl.util.compat import Sequence
|
from llnl.util.compat import Sequence
|
||||||
from llnl.util.lang import dedupe, memoized
|
from llnl.util.lang import dedupe, memoized
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
from spack.util.executable import Executable
|
from spack.util.executable import Executable
|
||||||
|
|
||||||
|
@ -508,7 +509,7 @@ def copy_tree(src, dest, symlinks=True, ignore=None, _permissions=False):
|
||||||
.format(target, new_target))
|
.format(target, new_target))
|
||||||
target = new_target
|
target = new_target
|
||||||
|
|
||||||
os.symlink(target, d)
|
symlink(target, d)
|
||||||
elif os.path.isdir(link_target):
|
elif os.path.isdir(link_target):
|
||||||
mkdirp(d)
|
mkdirp(d)
|
||||||
else:
|
else:
|
||||||
|
@ -806,10 +807,10 @@ def touchp(path):
|
||||||
|
|
||||||
def force_symlink(src, dest):
|
def force_symlink(src, dest):
|
||||||
try:
|
try:
|
||||||
os.symlink(src, dest)
|
symlink(src, dest)
|
||||||
except OSError:
|
except OSError:
|
||||||
os.remove(dest)
|
os.remove(dest)
|
||||||
os.symlink(src, dest)
|
symlink(src, dest)
|
||||||
|
|
||||||
|
|
||||||
def join_path(prefix, *args):
|
def join_path(prefix, *args):
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.filesystem import mkdirp, touch, traverse_tree
|
from llnl.util.filesystem import mkdirp, touch, traverse_tree
|
||||||
|
from llnl.util.symlink import islink, symlink
|
||||||
|
|
||||||
__all__ = ['LinkTree']
|
__all__ = ['LinkTree']
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
|
|
||||||
|
|
||||||
def remove_link(src, dest):
|
def remove_link(src, dest):
|
||||||
if not os.path.islink(dest):
|
if not islink(dest):
|
||||||
raise ValueError("%s is not a link tree!" % dest)
|
raise ValueError("%s is not a link tree!" % dest)
|
||||||
# remove if dest is a hardlink/symlink to src; this will only
|
# remove if dest is a hardlink/symlink to src; this will only
|
||||||
# be false if two packages are merged into a prefix and have a
|
# be false if two packages are merged into a prefix and have a
|
||||||
|
@ -113,7 +114,7 @@ def unmerge_directories(self, dest_root, ignore):
|
||||||
os.remove(marker)
|
os.remove(marker)
|
||||||
|
|
||||||
def merge(self, dest_root, ignore_conflicts=False, ignore=None,
|
def merge(self, dest_root, ignore_conflicts=False, ignore=None,
|
||||||
link=os.symlink, relative=False):
|
link=symlink, relative=False):
|
||||||
"""Link all files in src into dest, creating directories
|
"""Link all files in src into dest, creating directories
|
||||||
if necessary.
|
if necessary.
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ def merge(self, dest_root, ignore_conflicts=False, ignore=None,
|
||||||
ignore (callable): callable that returns True if a file is to be
|
ignore (callable): callable that returns True if a file is to be
|
||||||
ignored in the merge (by default ignore nothing)
|
ignored in the merge (by default ignore nothing)
|
||||||
|
|
||||||
link (callable): function to create links with (defaults to os.symlink)
|
link (callable): function to create links with (defaults to llnl.util.symlink)
|
||||||
|
|
||||||
relative (bool): create all symlinks relative to the target
|
relative (bool): create all symlinks relative to the target
|
||||||
(default False)
|
(default False)
|
||||||
|
|
139
lib/spack/llnl/util/symlink.py
Normal file
139
lib/spack/llnl/util/symlink.py
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
|
||||||
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
from os.path import exists, join
|
||||||
|
from sys import platform as _platform
|
||||||
|
|
||||||
|
is_windows = _platform == 'win32'
|
||||||
|
|
||||||
|
__win32_can_symlink__ = None
|
||||||
|
|
||||||
|
|
||||||
|
def symlink(real_path, link_path):
|
||||||
|
"""
|
||||||
|
Create a symbolic link.
|
||||||
|
|
||||||
|
On Windows, use junctions if os.symlink fails.
|
||||||
|
"""
|
||||||
|
if not is_windows or _win32_can_symlink():
|
||||||
|
os.symlink(real_path, link_path)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
# Try to use junctions
|
||||||
|
_win32_junction(real_path, link_path)
|
||||||
|
except OSError:
|
||||||
|
# If all else fails, fall back to copying files
|
||||||
|
shutil.copyfile(real_path, link_path)
|
||||||
|
|
||||||
|
|
||||||
|
def islink(path):
|
||||||
|
return os.path.islink(path) or _win32_is_junction(path)
|
||||||
|
|
||||||
|
|
||||||
|
# '_win32' functions based on
|
||||||
|
# https://github.com/Erotemic/ubelt/blob/master/ubelt/util_links.py
|
||||||
|
def _win32_junction(path, link):
|
||||||
|
# junctions require absolute paths
|
||||||
|
if not os.path.isabs(link):
|
||||||
|
link = os.path.abspath(link)
|
||||||
|
|
||||||
|
# os.symlink will fail if link exists, emulate the behavior here
|
||||||
|
if exists(link):
|
||||||
|
raise OSError(errno.EEXIST, 'File exists: %s -> %s' % (link, path))
|
||||||
|
|
||||||
|
if not os.path.isabs(path):
|
||||||
|
parent = os.path.join(link, os.pardir)
|
||||||
|
path = os.path.join(parent, path)
|
||||||
|
path = os.path.abspath(path)
|
||||||
|
|
||||||
|
if os.path.isdir(path):
|
||||||
|
# try using a junction
|
||||||
|
command = 'mklink /J "%s" "%s"' % (link, path)
|
||||||
|
else:
|
||||||
|
# try using a hard link
|
||||||
|
command = 'mklink /H "%s" "%s"' % (link, path)
|
||||||
|
|
||||||
|
_cmd(command)
|
||||||
|
|
||||||
|
|
||||||
|
def _win32_can_symlink():
|
||||||
|
global __win32_can_symlink__
|
||||||
|
if __win32_can_symlink__ is not None:
|
||||||
|
return __win32_can_symlink__
|
||||||
|
|
||||||
|
tempdir = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
dpath = join(tempdir, 'dpath')
|
||||||
|
fpath = join(tempdir, 'fpath.txt')
|
||||||
|
|
||||||
|
dlink = join(tempdir, 'dlink')
|
||||||
|
flink = join(tempdir, 'flink.txt')
|
||||||
|
|
||||||
|
import llnl.util.filesystem as fs
|
||||||
|
fs.touchp(fpath)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.symlink(dpath, dlink)
|
||||||
|
can_symlink_directories = os.path.islink(dlink)
|
||||||
|
except OSError:
|
||||||
|
can_symlink_directories = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.symlink(fpath, flink)
|
||||||
|
can_symlink_files = os.path.islink(flink)
|
||||||
|
except OSError:
|
||||||
|
can_symlink_files = False
|
||||||
|
|
||||||
|
# Cleanup the test directory
|
||||||
|
shutil.rmtree(tempdir)
|
||||||
|
|
||||||
|
__win32_can_symlink__ = can_symlink_directories and can_symlink_files
|
||||||
|
|
||||||
|
return __win32_can_symlink__
|
||||||
|
|
||||||
|
|
||||||
|
def _win32_is_junction(path):
|
||||||
|
"""
|
||||||
|
Determines if a path is a win32 junction
|
||||||
|
"""
|
||||||
|
if os.path.islink(path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if is_windows:
|
||||||
|
import ctypes.wintypes
|
||||||
|
|
||||||
|
GetFileAttributes = ctypes.windll.kernel32.GetFileAttributesW
|
||||||
|
GetFileAttributes.argtypes = (ctypes.wintypes.LPWSTR,)
|
||||||
|
GetFileAttributes.restype = ctypes.wintypes.DWORD
|
||||||
|
|
||||||
|
INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF
|
||||||
|
FILE_ATTRIBUTE_REPARSE_POINT = 0x400
|
||||||
|
|
||||||
|
res = GetFileAttributes(path)
|
||||||
|
return res != INVALID_FILE_ATTRIBUTES and \
|
||||||
|
bool(res & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# Based on https://github.com/Erotemic/ubelt/blob/master/ubelt/util_cmd.py
|
||||||
|
def _cmd(command):
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Create a new process to execute the command
|
||||||
|
def make_proc():
|
||||||
|
# delay the creation of the process until we validate all args
|
||||||
|
proc = subprocess.Popen(command, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE, shell=True,
|
||||||
|
universal_newlines=True, cwd=None, env=None)
|
||||||
|
return proc
|
||||||
|
|
||||||
|
proc = make_proc()
|
||||||
|
(out, err) = proc.communicate()
|
||||||
|
if proc.wait() != 0:
|
||||||
|
raise OSError(str(err))
|
|
@ -46,6 +46,7 @@
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.filesystem import install, install_tree, mkdirp
|
from llnl.util.filesystem import install, install_tree, mkdirp
|
||||||
from llnl.util.lang import dedupe
|
from llnl.util.lang import dedupe
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
from llnl.util.tty.color import cescape, colorize
|
from llnl.util.tty.color import cescape, colorize
|
||||||
from llnl.util.tty.log import MultiProcessFd
|
from llnl.util.tty.log import MultiProcessFd
|
||||||
|
|
||||||
|
@ -545,7 +546,7 @@ def _set_variables_for_single_module(pkg, module):
|
||||||
m.makedirs = os.makedirs
|
m.makedirs = os.makedirs
|
||||||
m.remove = os.remove
|
m.remove = os.remove
|
||||||
m.removedirs = os.removedirs
|
m.removedirs = os.removedirs
|
||||||
m.symlink = os.symlink
|
m.symlink = symlink
|
||||||
|
|
||||||
m.mkdirp = mkdirp
|
m.mkdirp = mkdirp
|
||||||
m.install = install
|
m.install = install
|
||||||
|
@ -668,10 +669,10 @@ def _static_to_shared_library(arch, compiler, static_lib, shared_lib=None,
|
||||||
shared_lib_link = os.path.basename(shared_lib)
|
shared_lib_link = os.path.basename(shared_lib)
|
||||||
|
|
||||||
if version or compat_version:
|
if version or compat_version:
|
||||||
os.symlink(shared_lib_link, shared_lib_base)
|
symlink(shared_lib_link, shared_lib_base)
|
||||||
|
|
||||||
if compat_version and compat_version != version:
|
if compat_version and compat_version != version:
|
||||||
os.symlink(shared_lib_link, '{0}.{1}'.format(shared_lib_base,
|
symlink(shared_lib_link, '{0}.{1}'.format(shared_lib_base,
|
||||||
compat_version))
|
compat_version))
|
||||||
|
|
||||||
return compiler(*compiler_args, output=compiler_output)
|
return compiler(*compiler_args, output=compiler_output)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import llnl.util.lang
|
import llnl.util.lang
|
||||||
from llnl.util.filesystem import mkdirp
|
from llnl.util.filesystem import mkdirp
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.error
|
import spack.error
|
||||||
|
@ -85,7 +86,7 @@ def symlink(self, mirror_ref):
|
||||||
# to https://github.com/spack/spack/pull/13908)
|
# to https://github.com/spack/spack/pull/13908)
|
||||||
os.unlink(cosmetic_path)
|
os.unlink(cosmetic_path)
|
||||||
mkdirp(os.path.dirname(cosmetic_path))
|
mkdirp(os.path.dirname(cosmetic_path))
|
||||||
os.symlink(relative_dst, cosmetic_path)
|
symlink(relative_dst, cosmetic_path)
|
||||||
|
|
||||||
|
|
||||||
#: Spack's local cache for downloaded source archives
|
#: Spack's local cache for downloaded source archives
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
import spack.cmd.common.arguments as arguments
|
import spack.cmd.common.arguments as arguments
|
||||||
|
@ -123,7 +124,7 @@ def deprecate(parser, args):
|
||||||
if not answer:
|
if not answer:
|
||||||
tty.die('Will not deprecate any packages.')
|
tty.die('Will not deprecate any packages.')
|
||||||
|
|
||||||
link_fn = os.link if args.link_type == 'hard' else os.symlink
|
link_fn = os.link if args.link_type == 'hard' else symlink
|
||||||
|
|
||||||
for dcate, dcator in zip(all_deprecate, all_deprecators):
|
for dcate, dcator in zip(all_deprecate, all_deprecators):
|
||||||
dcate.package.do_deprecate(dcator, link_fn)
|
dcate.package.do_deprecate(dcator, link_fn)
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
import os
|
||||||
|
|
||||||
|
import llnl.util.filesystem
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.cmd.common.arguments
|
import spack.cmd.common.arguments
|
||||||
import spack.cmd.modules
|
import spack.cmd.modules
|
||||||
|
@ -56,3 +60,11 @@ def setdefault(module_type, specs, args):
|
||||||
with spack.config.override(scope):
|
with spack.config.override(scope):
|
||||||
writer = spack.modules.module_types['lmod'](spec, args.module_set_name)
|
writer = spack.modules.module_types['lmod'](spec, args.module_set_name)
|
||||||
writer.update_module_defaults()
|
writer.update_module_defaults()
|
||||||
|
|
||||||
|
|
||||||
|
module_folder = os.path.dirname(writer.layout.filename)
|
||||||
|
module_basename = os.path.basename(writer.layout.filename)
|
||||||
|
with llnl.util.filesystem.working_dir(module_folder):
|
||||||
|
if os.path.exists('default') and os.path.islink('default'):
|
||||||
|
os.remove('default')
|
||||||
|
symlink(module_basename, 'default')
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import llnl.util.lang
|
import llnl.util.lang
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.compiler
|
import spack.compiler
|
||||||
import spack.compilers.clang
|
import spack.compilers.clang
|
||||||
|
@ -162,10 +163,10 @@ def setup_custom_environment(self, pkg, env):
|
||||||
for fname in os.listdir(dev_dir):
|
for fname in os.listdir(dev_dir):
|
||||||
if fname in bins:
|
if fname in bins:
|
||||||
os.unlink(os.path.join(dev_dir, fname))
|
os.unlink(os.path.join(dev_dir, fname))
|
||||||
os.symlink(
|
symlink(
|
||||||
os.path.join(spack.paths.build_env_path, 'cc'),
|
os.path.join(spack.paths.build_env_path, 'cc'),
|
||||||
os.path.join(dev_dir, fname))
|
os.path.join(dev_dir, fname))
|
||||||
|
|
||||||
os.symlink(developer_root, xcode_link)
|
symlink(developer_root, xcode_link)
|
||||||
|
|
||||||
env.set('DEVELOPER_DIR', xcode_link)
|
env.set('DEVELOPER_DIR', xcode_link)
|
||||||
|
|
|
@ -293,6 +293,7 @@ def _write_section(self, section):
|
||||||
syaml.dump_config(data_to_write, stream=f,
|
syaml.dump_config(data_to_write, stream=f,
|
||||||
default_flow_style=False)
|
default_flow_style=False)
|
||||||
rename(tmp, self.path)
|
rename(tmp, self.path)
|
||||||
|
|
||||||
except (yaml.YAMLError, IOError) as e:
|
except (yaml.YAMLError, IOError) as e:
|
||||||
raise ConfigFileError(
|
raise ConfigFileError(
|
||||||
"Error writing to config file: '%s'" % str(e))
|
"Error writing to config file: '%s'" % str(e))
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.lang import dedupe
|
from llnl.util.lang import dedupe
|
||||||
|
from llnl.util.symlink import islink, symlink
|
||||||
|
|
||||||
import spack.bootstrap
|
import spack.bootstrap
|
||||||
import spack.compilers
|
import spack.compilers
|
||||||
|
@ -1461,7 +1462,7 @@ def _install_log_links(self, spec):
|
||||||
log_path, '%s-%s.log' % (spec.name, spec.dag_hash(7)))
|
log_path, '%s-%s.log' % (spec.name, spec.dag_hash(7)))
|
||||||
if os.path.lexists(build_log_link):
|
if os.path.lexists(build_log_link):
|
||||||
os.remove(build_log_link)
|
os.remove(build_log_link)
|
||||||
os.symlink(spec.package.build_log_path, build_log_link)
|
symlink(spec.package.build_log_path, build_log_link)
|
||||||
|
|
||||||
def uninstalled_specs(self):
|
def uninstalled_specs(self):
|
||||||
"""Return a list of all uninstalled (and non-dev) specs."""
|
"""Return a list of all uninstalled (and non-dev) specs."""
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
temp_rename,
|
temp_rename,
|
||||||
working_dir,
|
working_dir,
|
||||||
)
|
)
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.error
|
import spack.error
|
||||||
|
@ -640,7 +641,7 @@ def fetch(self):
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
|
|
||||||
# Symlink to local cached archive.
|
# Symlink to local cached archive.
|
||||||
os.symlink(path, filename)
|
symlink(path, filename)
|
||||||
|
|
||||||
# Remove link if checksum fails, or subsequent fetchers
|
# Remove link if checksum fails, or subsequent fetchers
|
||||||
# will assume they don't need to download.
|
# will assume they don't need to download.
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
from llnl.util.filesystem import mkdirp, remove_dead_links, remove_empty_directories
|
from llnl.util.filesystem import mkdirp, remove_dead_links, remove_empty_directories
|
||||||
from llnl.util.lang import index_by, match_predicate
|
from llnl.util.lang import index_by, match_predicate
|
||||||
from llnl.util.link_tree import LinkTree, MergeConflictError
|
from llnl.util.link_tree import LinkTree, MergeConflictError
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
from llnl.util.tty.color import colorize
|
from llnl.util.tty.color import colorize
|
||||||
|
|
||||||
import spack.config
|
import spack.config
|
||||||
|
@ -39,7 +40,7 @@
|
||||||
def view_symlink(src, dst, **kwargs):
|
def view_symlink(src, dst, **kwargs):
|
||||||
# keyword arguments are irrelevant
|
# keyword arguments are irrelevant
|
||||||
# here to fit required call signature
|
# here to fit required call signature
|
||||||
os.symlink(src, dst)
|
symlink(src, dst)
|
||||||
|
|
||||||
|
|
||||||
def view_hardlink(src, dst, **kwargs):
|
def view_hardlink(src, dst, **kwargs):
|
||||||
|
@ -141,7 +142,7 @@ def __init__(self, root, layout, **kwargs):
|
||||||
Initialize a filesystem view under the given `root` directory with
|
Initialize a filesystem view under the given `root` directory with
|
||||||
corresponding directory `layout`.
|
corresponding directory `layout`.
|
||||||
|
|
||||||
Files are linked by method `link` (os.symlink by default).
|
Files are linked by method `link` (llnl.util.symlink by default).
|
||||||
"""
|
"""
|
||||||
self._root = root
|
self._root = root
|
||||||
self.layout = layout
|
self.layout = layout
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
from llnl.util.filesystem import mkdirp
|
from llnl.util.filesystem import mkdirp
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
from spack.util.editor import editor
|
from spack.util.editor import editor
|
||||||
from spack.util.executable import Executable, which
|
from spack.util.executable import Executable, which
|
||||||
|
@ -179,6 +180,6 @@ def symlink_license(pkg):
|
||||||
os.remove(link_name)
|
os.remove(link_name)
|
||||||
|
|
||||||
if os.path.exists(target):
|
if os.path.exists(target):
|
||||||
os.symlink(target, link_name)
|
symlink(target, link_name)
|
||||||
tty.msg("Added local symlink %s to global license file" %
|
tty.msg("Added local symlink %s to global license file" %
|
||||||
link_name)
|
link_name)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
import llnl.util.lang
|
import llnl.util.lang
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.bootstrap
|
import spack.bootstrap
|
||||||
import spack.platforms
|
import spack.platforms
|
||||||
|
@ -683,7 +684,7 @@ def make_link_relative(new_links, orig_links):
|
||||||
target = os.readlink(orig_link)
|
target = os.readlink(orig_link)
|
||||||
relative_target = os.path.relpath(target, os.path.dirname(orig_link))
|
relative_target = os.path.relpath(target, os.path.dirname(orig_link))
|
||||||
os.unlink(new_link)
|
os.unlink(new_link)
|
||||||
os.symlink(relative_target, new_link)
|
symlink(relative_target, new_link)
|
||||||
|
|
||||||
|
|
||||||
def make_macho_binaries_relative(cur_path_names, orig_path_names,
|
def make_macho_binaries_relative(cur_path_names, orig_path_names,
|
||||||
|
@ -764,7 +765,7 @@ def relocate_links(links, orig_layout_root,
|
||||||
orig_install_prefix, new_install_prefix, link_target
|
orig_install_prefix, new_install_prefix, link_target
|
||||||
)
|
)
|
||||||
os.unlink(abs_link)
|
os.unlink(abs_link)
|
||||||
os.symlink(link_target, abs_link)
|
symlink(link_target, abs_link)
|
||||||
|
|
||||||
# If the link is absolute and has not been relocated then
|
# If the link is absolute and has not been relocated then
|
||||||
# warn the user about that
|
# warn the user about that
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.paths
|
import spack.paths
|
||||||
|
|
||||||
|
@ -36,9 +37,9 @@ def stage(tmpdir_factory):
|
||||||
fs.touchp('source/g/i/j/10')
|
fs.touchp('source/g/i/j/10')
|
||||||
|
|
||||||
# Create symlinks
|
# Create symlinks
|
||||||
os.symlink(os.path.abspath('source/1'), 'source/2')
|
symlink(os.path.abspath('source/1'), 'source/2')
|
||||||
os.symlink('b/2', 'source/a/b2')
|
symlink('b/2', 'source/a/b2')
|
||||||
os.symlink('a/b', 'source/f')
|
symlink('a/b', 'source/f')
|
||||||
|
|
||||||
# Create destination directory
|
# Create destination directory
|
||||||
fs.mkdirp('dest')
|
fs.mkdirp('dest')
|
||||||
|
@ -174,12 +175,18 @@ def test_symlinks_true(self, stage):
|
||||||
fs.copy_tree('source', 'dest', symlinks=True)
|
fs.copy_tree('source', 'dest', symlinks=True)
|
||||||
|
|
||||||
assert os.path.exists('dest/2')
|
assert os.path.exists('dest/2')
|
||||||
|
if sys.platform != "win32":
|
||||||
|
# TODO: islink will return false for junctions
|
||||||
assert os.path.islink('dest/2')
|
assert os.path.islink('dest/2')
|
||||||
|
|
||||||
assert os.path.exists('dest/a/b2')
|
assert os.path.exists('dest/a/b2')
|
||||||
|
if sys.platform != "win32":
|
||||||
|
# TODO: Not supported for junctions ?
|
||||||
with fs.working_dir('dest/a'):
|
with fs.working_dir('dest/a'):
|
||||||
assert os.path.exists(os.readlink('b2'))
|
assert os.path.exists(os.readlink('b2'))
|
||||||
|
|
||||||
|
if sys.platform != "win32":
|
||||||
|
# TODO: Not supported on Windows ?
|
||||||
assert (os.path.realpath('dest/f/2') ==
|
assert (os.path.realpath('dest/f/2') ==
|
||||||
os.path.abspath('dest/a/b/2'))
|
os.path.abspath('dest/a/b/2'))
|
||||||
assert os.path.realpath('dest/2') == os.path.abspath('dest/1')
|
assert os.path.realpath('dest/2') == os.path.abspath('dest/1')
|
||||||
|
@ -201,6 +208,7 @@ def test_symlinks_false(self, stage):
|
||||||
fs.copy_tree('source', 'dest', symlinks=False)
|
fs.copy_tree('source', 'dest', symlinks=False)
|
||||||
|
|
||||||
assert os.path.exists('dest/2')
|
assert os.path.exists('dest/2')
|
||||||
|
if sys.platform != "win32":
|
||||||
assert not os.path.islink('dest/2')
|
assert not os.path.islink('dest/2')
|
||||||
|
|
||||||
def test_glob_src(self, stage):
|
def test_glob_src(self, stage):
|
||||||
|
@ -258,6 +266,7 @@ def test_symlinks_true(self, stage):
|
||||||
fs.install_tree('source', 'dest', symlinks=True)
|
fs.install_tree('source', 'dest', symlinks=True)
|
||||||
|
|
||||||
assert os.path.exists('dest/2')
|
assert os.path.exists('dest/2')
|
||||||
|
if sys.platform != "win32":
|
||||||
assert os.path.islink('dest/2')
|
assert os.path.islink('dest/2')
|
||||||
check_added_exe_permissions('source/2', 'dest/2')
|
check_added_exe_permissions('source/2', 'dest/2')
|
||||||
|
|
||||||
|
@ -268,6 +277,7 @@ def test_symlinks_false(self, stage):
|
||||||
fs.install_tree('source', 'dest', symlinks=False)
|
fs.install_tree('source', 'dest', symlinks=False)
|
||||||
|
|
||||||
assert os.path.exists('dest/2')
|
assert os.path.exists('dest/2')
|
||||||
|
if sys.platform != "win32":
|
||||||
assert not os.path.islink('dest/2')
|
assert not os.path.islink('dest/2')
|
||||||
check_added_exe_permissions('source/2', 'dest/2')
|
check_added_exe_permissions('source/2', 'dest/2')
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
from llnl.util.filesystem import mkdirp, touchp, working_dir
|
from llnl.util.filesystem import mkdirp, touchp, working_dir
|
||||||
from llnl.util.link_tree import LinkTree
|
from llnl.util.link_tree import LinkTree
|
||||||
|
from llnl.util.symlink import islink
|
||||||
|
|
||||||
from spack.stage import Stage
|
from spack.stage import Stage
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ def link_tree(stage):
|
||||||
|
|
||||||
def check_file_link(filename, expected_target):
|
def check_file_link(filename, expected_target):
|
||||||
assert os.path.isfile(filename)
|
assert os.path.isfile(filename)
|
||||||
assert os.path.islink(filename)
|
assert islink(filename)
|
||||||
assert (os.path.abspath(os.path.realpath(filename)) ==
|
assert (os.path.abspath(os.path.realpath(filename)) ==
|
||||||
os.path.abspath(expected_target))
|
os.path.abspath(expected_target))
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from llnl.util.filesystem import mkdirp
|
from llnl.util.filesystem import mkdirp
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.binary_distribution as bindist
|
import spack.binary_distribution as bindist
|
||||||
import spack.cmd.buildcache as buildcache
|
import spack.cmd.buildcache as buildcache
|
||||||
|
@ -79,7 +80,7 @@ def test_buildcache(mock_archive, tmpdir):
|
||||||
|
|
||||||
# Create an absolute symlink
|
# Create an absolute symlink
|
||||||
linkname = os.path.join(spec.prefix, "link_to_dummy.txt")
|
linkname = os.path.join(spec.prefix, "link_to_dummy.txt")
|
||||||
os.symlink(filename, linkname)
|
symlink(filename, linkname)
|
||||||
|
|
||||||
# Create the build cache and
|
# Create the build cache and
|
||||||
# put it directly into the mirror
|
# put it directly into the mirror
|
||||||
|
@ -232,8 +233,8 @@ def test_relocate_links(tmpdir):
|
||||||
with open(new_binname, 'w') as f:
|
with open(new_binname, 'w') as f:
|
||||||
f.write('\n')
|
f.write('\n')
|
||||||
os.utime(new_binname, None)
|
os.utime(new_binname, None)
|
||||||
os.symlink(old_binname, new_linkname)
|
symlink(old_binname, new_linkname)
|
||||||
os.symlink('/usr/lib/libc.so', new_linkname2)
|
symlink('/usr/lib/libc.so', new_linkname2)
|
||||||
relocate_links(filenames, old_layout_root,
|
relocate_links(filenames, old_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_linkname) == new_binname
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
import llnl.util.filesystem as fs
|
import llnl.util.filesystem as fs
|
||||||
|
from llnl.util.symlink import symlink
|
||||||
|
|
||||||
import spack.spec
|
import spack.spec
|
||||||
import spack.store
|
import spack.store
|
||||||
|
@ -21,7 +22,7 @@ def test_link_manifest_entry(tmpdir):
|
||||||
file = str(tmpdir.join('file'))
|
file = str(tmpdir.join('file'))
|
||||||
open(file, 'a').close()
|
open(file, 'a').close()
|
||||||
link = str(tmpdir.join('link'))
|
link = str(tmpdir.join('link'))
|
||||||
os.symlink(file, link)
|
symlink(file, link)
|
||||||
|
|
||||||
data = spack.verify.create_manifest_entry(link)
|
data = spack.verify.create_manifest_entry(link)
|
||||||
assert data['type'] == 'link'
|
assert data['type'] == 'link'
|
||||||
|
@ -43,7 +44,7 @@ def test_link_manifest_entry(tmpdir):
|
||||||
file2 = str(tmpdir.join('file2'))
|
file2 = str(tmpdir.join('file2'))
|
||||||
open(file2, 'a').close()
|
open(file2, 'a').close()
|
||||||
os.remove(link)
|
os.remove(link)
|
||||||
os.symlink(file2, link)
|
symlink(file2, link)
|
||||||
|
|
||||||
results = spack.verify.check_entry(link, data)
|
results = spack.verify.check_entry(link, data)
|
||||||
assert results.has_errors()
|
assert results.has_errors()
|
||||||
|
@ -157,7 +158,7 @@ def test_check_prefix_manifest(tmpdir):
|
||||||
f.write("I'm a little file short and stout")
|
f.write("I'm a little file short and stout")
|
||||||
|
|
||||||
link = os.path.join(bin_dir, 'run')
|
link = os.path.join(bin_dir, 'run')
|
||||||
os.symlink(file, link)
|
symlink(file, link)
|
||||||
|
|
||||||
spack.verify.write_manifest(spec)
|
spack.verify.write_manifest(spec)
|
||||||
results = spack.verify.check_spec_manifest(spec)
|
results = spack.verify.check_spec_manifest(spec)
|
||||||
|
|
Loading…
Reference in a new issue