summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBetsy McPhail <betsy.mcphail@kitware.com>2021-10-22 12:16:11 -0400
committerPeter Scheibel <scheibel1@llnl.gov>2022-03-17 09:01:01 -0700
commitfb0e91c5341261b89aa2c410016f83cefe32bdef (patch)
treee16a7eeb2e78cdfd05fdd680fb0902e51278df5b /lib
parenta7de2fa380dd397a0a1c3e23629074adf06d2604 (diff)
downloadspack-fb0e91c5341261b89aa2c410016f83cefe32bdef.tar.gz
spack-fb0e91c5341261b89aa2c410016f83cefe32bdef.tar.bz2
spack-fb0e91c5341261b89aa2c410016f83cefe32bdef.tar.xz
spack-fb0e91c5341261b89aa2c410016f83cefe32bdef.zip
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>
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/external/macholib/util.py4
-rw-r--r--lib/spack/external/pytest-fallback/py/_path/local.py8
-rw-r--r--lib/spack/llnl/util/filesystem.py7
-rw-r--r--lib/spack/llnl/util/link_tree.py7
-rw-r--r--lib/spack/llnl/util/symlink.py139
-rw-r--r--lib/spack/spack/build_environment.py9
-rw-r--r--lib/spack/spack/caches.py3
-rw-r--r--lib/spack/spack/cmd/deprecate.py3
-rw-r--r--lib/spack/spack/cmd/modules/lmod.py12
-rw-r--r--lib/spack/spack/compilers/apple_clang.py5
-rw-r--r--lib/spack/spack/config.py1
-rw-r--r--lib/spack/spack/environment/environment.py3
-rw-r--r--lib/spack/spack/fetch_strategy.py3
-rw-r--r--lib/spack/spack/filesystem_view.py5
-rw-r--r--lib/spack/spack/hooks/licensing.py3
-rw-r--r--lib/spack/spack/relocate.py5
-rw-r--r--lib/spack/spack/test/llnl/util/filesystem.py34
-rw-r--r--lib/spack/spack/test/llnl/util/link_tree.py3
-rw-r--r--lib/spack/spack/test/packaging.py7
-rw-r--r--lib/spack/spack/test/verification.py7
20 files changed, 224 insertions, 44 deletions
diff --git a/lib/spack/external/macholib/util.py b/lib/spack/external/macholib/util.py
index d5ab33544a..4eb4bd60e7 100644
--- a/lib/spack/external/macholib/util.py
+++ b/lib/spack/external/macholib/util.py
@@ -6,6 +6,8 @@ import sys
from macholib import mach_o
+from llnl.util.symlink import symlink
+
MAGIC = [
struct.pack("!L", getattr(mach_o, "MH_" + _))
for _ in ["MAGIC", "CIGAM", "MAGIC_64", "CIGAM_64"]
@@ -140,7 +142,7 @@ def mergetree(src, dst, condition=None, copyfn=mergecopy, srcbase=None):
try:
if os.path.islink(srcname):
realsrc = os.readlink(srcname)
- os.symlink(realsrc, dstname)
+ symlink(realsrc, dstname)
elif os.path.isdir(srcname):
mergetree(
srcname,
diff --git a/lib/spack/external/pytest-fallback/py/_path/local.py b/lib/spack/external/pytest-fallback/py/_path/local.py
index d2f16b993e..72ffb9f6cb 100644
--- a/lib/spack/external/pytest-fallback/py/_path/local.py
+++ b/lib/spack/external/pytest-fallback/py/_path/local.py
@@ -12,6 +12,8 @@ from stat import S_ISLNK, S_ISDIR, S_ISREG
from os.path import abspath, normpath, isabs, exists, isdir, isfile, islink, dirname
+from llnl.util.symlink import symlink
+
if sys.version_info > (3,0):
def map_as_list(func, iter):
return list(map(func, iter))
@@ -79,7 +81,7 @@ class PosixPath(common.PathBase):
def mksymlinkto(self, value, absolute=1):
""" create a symbolic link with the given value (pointing to another name). """
if absolute:
- py.error.checked_call(os.symlink, str(value), self.strpath)
+ py.error.checked_call(symlink, str(value), self.strpath)
else:
base = self.common(value)
# with posix local paths '/' is always a common base
@@ -87,7 +89,7 @@ class PosixPath(common.PathBase):
reldest = self.relto(base)
n = reldest.count(self.sep)
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):
import pwd
@@ -892,7 +894,7 @@ class LocalPath(FSBase):
except OSError:
pass
try:
- os.symlink(src, dest)
+ symlink(src, dest)
except (OSError, AttributeError, NotImplementedError):
pass
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index 9ce56d91fa..d18a06461f 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -24,6 +24,7 @@ import six
from llnl.util import tty
from llnl.util.compat import Sequence
from llnl.util.lang import dedupe, memoized
+from llnl.util.symlink import symlink
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))
target = new_target
- os.symlink(target, d)
+ symlink(target, d)
elif os.path.isdir(link_target):
mkdirp(d)
else:
@@ -806,10 +807,10 @@ def touchp(path):
def force_symlink(src, dest):
try:
- os.symlink(src, dest)
+ symlink(src, dest)
except OSError:
os.remove(dest)
- os.symlink(src, dest)
+ symlink(src, dest)
def join_path(prefix, *args):
diff --git a/lib/spack/llnl/util/link_tree.py b/lib/spack/llnl/util/link_tree.py
index 2a670423cb..bd91a1dabc 100644
--- a/lib/spack/llnl/util/link_tree.py
+++ b/lib/spack/llnl/util/link_tree.py
@@ -13,6 +13,7 @@ import shutil
import llnl.util.tty as tty
from llnl.util.filesystem import mkdirp, touch, traverse_tree
+from llnl.util.symlink import islink, symlink
__all__ = ['LinkTree']
@@ -20,7 +21,7 @@ empty_file_name = '.spack-empty'
def remove_link(src, dest):
- if not os.path.islink(dest):
+ if not islink(dest):
raise ValueError("%s is not a link tree!" % dest)
# 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
@@ -113,7 +114,7 @@ class LinkTree(object):
os.remove(marker)
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
if necessary.
@@ -125,7 +126,7 @@ class LinkTree(object):
ignore (callable): callable that returns True if a file is to be
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
(default False)
diff --git a/lib/spack/llnl/util/symlink.py b/lib/spack/llnl/util/symlink.py
new file mode 100644
index 0000000000..3e5f0d4868
--- /dev/null
+++ b/lib/spack/llnl/util/symlink.py
@@ -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))
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index a6757c9deb..542c0a504d 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -46,6 +46,7 @@ from six import StringIO
import llnl.util.tty as tty
from llnl.util.filesystem import install, install_tree, mkdirp
from llnl.util.lang import dedupe
+from llnl.util.symlink import symlink
from llnl.util.tty.color import cescape, colorize
from llnl.util.tty.log import MultiProcessFd
@@ -545,7 +546,7 @@ def _set_variables_for_single_module(pkg, module):
m.makedirs = os.makedirs
m.remove = os.remove
m.removedirs = os.removedirs
- m.symlink = os.symlink
+ m.symlink = symlink
m.mkdirp = mkdirp
m.install = install
@@ -668,11 +669,11 @@ def _static_to_shared_library(arch, compiler, static_lib, shared_lib=None,
shared_lib_link = os.path.basename(shared_lib)
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:
- os.symlink(shared_lib_link, '{0}.{1}'.format(shared_lib_base,
- compat_version))
+ symlink(shared_lib_link, '{0}.{1}'.format(shared_lib_base,
+ compat_version))
return compiler(*compiler_args, output=compiler_output)
diff --git a/lib/spack/spack/caches.py b/lib/spack/spack/caches.py
index 0793ba8205..8112a97ea3 100644
--- a/lib/spack/spack/caches.py
+++ b/lib/spack/spack/caches.py
@@ -8,6 +8,7 @@ import os
import llnl.util.lang
from llnl.util.filesystem import mkdirp
+from llnl.util.symlink import symlink
import spack.config
import spack.error
@@ -85,7 +86,7 @@ class MirrorCache(object):
# to https://github.com/spack/spack/pull/13908)
os.unlink(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
diff --git a/lib/spack/spack/cmd/deprecate.py b/lib/spack/spack/cmd/deprecate.py
index 2732273498..67cb24be7e 100644
--- a/lib/spack/spack/cmd/deprecate.py
+++ b/lib/spack/spack/cmd/deprecate.py
@@ -19,6 +19,7 @@ import argparse
import os
import llnl.util.tty as tty
+from llnl.util.symlink import symlink
import spack.cmd
import spack.cmd.common.arguments as arguments
@@ -123,7 +124,7 @@ def deprecate(parser, args):
if not answer:
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):
dcate.package.do_deprecate(dcator, link_fn)
diff --git a/lib/spack/spack/cmd/modules/lmod.py b/lib/spack/spack/cmd/modules/lmod.py
index 8555822826..abd89b3a18 100644
--- a/lib/spack/spack/cmd/modules/lmod.py
+++ b/lib/spack/spack/cmd/modules/lmod.py
@@ -4,6 +4,10 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import functools
+import os
+
+import llnl.util.filesystem
+from llnl.util.symlink import symlink
import spack.cmd.common.arguments
import spack.cmd.modules
@@ -56,3 +60,11 @@ def setdefault(module_type, specs, args):
with spack.config.override(scope):
writer = spack.modules.module_types['lmod'](spec, args.module_set_name)
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')
diff --git a/lib/spack/spack/compilers/apple_clang.py b/lib/spack/spack/compilers/apple_clang.py
index a60105dccb..e9efd89c49 100644
--- a/lib/spack/spack/compilers/apple_clang.py
+++ b/lib/spack/spack/compilers/apple_clang.py
@@ -8,6 +8,7 @@ import shutil
import llnl.util.lang
import llnl.util.tty as tty
+from llnl.util.symlink import symlink
import spack.compiler
import spack.compilers.clang
@@ -162,10 +163,10 @@ class AppleClang(spack.compilers.clang.Clang):
for fname in os.listdir(dev_dir):
if fname in bins:
os.unlink(os.path.join(dev_dir, fname))
- os.symlink(
+ symlink(
os.path.join(spack.paths.build_env_path, 'cc'),
os.path.join(dev_dir, fname))
- os.symlink(developer_root, xcode_link)
+ symlink(developer_root, xcode_link)
env.set('DEVELOPER_DIR', xcode_link)
diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py
index 4048c3b192..fa6846e207 100644
--- a/lib/spack/spack/config.py
+++ b/lib/spack/spack/config.py
@@ -293,6 +293,7 @@ class SingleFileScope(ConfigScope):
syaml.dump_config(data_to_write, stream=f,
default_flow_style=False)
rename(tmp, self.path)
+
except (yaml.YAMLError, IOError) as e:
raise ConfigFileError(
"Error writing to config file: '%s'" % str(e))
diff --git a/lib/spack/spack/environment/environment.py b/lib/spack/spack/environment/environment.py
index 4f2a08a107..9d6c5ba8c8 100644
--- a/lib/spack/spack/environment/environment.py
+++ b/lib/spack/spack/environment/environment.py
@@ -17,6 +17,7 @@ import six
import llnl.util.filesystem as fs
import llnl.util.tty as tty
from llnl.util.lang import dedupe
+from llnl.util.symlink import islink, symlink
import spack.bootstrap
import spack.compilers
@@ -1461,7 +1462,7 @@ class Environment(object):
log_path, '%s-%s.log' % (spec.name, spec.dag_hash(7)))
if os.path.lexists(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):
"""Return a list of all uninstalled (and non-dev) specs."""
diff --git a/lib/spack/spack/fetch_strategy.py b/lib/spack/spack/fetch_strategy.py
index 1204932dda..f9823fb648 100644
--- a/lib/spack/spack/fetch_strategy.py
+++ b/lib/spack/spack/fetch_strategy.py
@@ -43,6 +43,7 @@ from llnl.util.filesystem import (
temp_rename,
working_dir,
)
+from llnl.util.symlink import symlink
import spack.config
import spack.error
@@ -640,7 +641,7 @@ class CacheURLFetchStrategy(URLFetchStrategy):
os.remove(filename)
# Symlink to local cached archive.
- os.symlink(path, filename)
+ symlink(path, filename)
# Remove link if checksum fails, or subsequent fetchers
# will assume they don't need to download.
diff --git a/lib/spack/spack/filesystem_view.py b/lib/spack/spack/filesystem_view.py
index a95f9f9f6d..d6b3d3ed8f 100644
--- a/lib/spack/spack/filesystem_view.py
+++ b/lib/spack/spack/filesystem_view.py
@@ -15,6 +15,7 @@ from llnl.util.compat import filter, map, zip
from llnl.util.filesystem import mkdirp, remove_dead_links, remove_empty_directories
from llnl.util.lang import index_by, match_predicate
from llnl.util.link_tree import LinkTree, MergeConflictError
+from llnl.util.symlink import symlink
from llnl.util.tty.color import colorize
import spack.config
@@ -39,7 +40,7 @@ _projections_path = '.spack/projections.yaml'
def view_symlink(src, dst, **kwargs):
# keyword arguments are irrelevant
# here to fit required call signature
- os.symlink(src, dst)
+ symlink(src, dst)
def view_hardlink(src, dst, **kwargs):
@@ -141,7 +142,7 @@ class FilesystemView(object):
Initialize a filesystem view under the given `root` directory with
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.layout = layout
diff --git a/lib/spack/spack/hooks/licensing.py b/lib/spack/spack/hooks/licensing.py
index 01eacdeec0..2312811afe 100644
--- a/lib/spack/spack/hooks/licensing.py
+++ b/lib/spack/spack/hooks/licensing.py
@@ -7,6 +7,7 @@ import os
import llnl.util.tty as tty
from llnl.util.filesystem import mkdirp
+from llnl.util.symlink import symlink
from spack.util.editor import editor
from spack.util.executable import Executable, which
@@ -179,6 +180,6 @@ def symlink_license(pkg):
os.remove(link_name)
if os.path.exists(target):
- os.symlink(target, link_name)
+ symlink(target, link_name)
tty.msg("Added local symlink %s to global license file" %
link_name)
diff --git a/lib/spack/spack/relocate.py b/lib/spack/spack/relocate.py
index 02bd0edf1f..bbb5e8025a 100644
--- a/lib/spack/spack/relocate.py
+++ b/lib/spack/spack/relocate.py
@@ -13,6 +13,7 @@ import macholib.MachO
import llnl.util.lang
import llnl.util.tty as tty
+from llnl.util.symlink import symlink
import spack.bootstrap
import spack.platforms
@@ -683,7 +684,7 @@ def make_link_relative(new_links, orig_links):
target = os.readlink(orig_link)
relative_target = os.path.relpath(target, os.path.dirname(orig_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,
@@ -764,7 +765,7 @@ def relocate_links(links, orig_layout_root,
orig_install_prefix, new_install_prefix, link_target
)
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
# warn the user about that
diff --git a/lib/spack/spack/test/llnl/util/filesystem.py b/lib/spack/spack/test/llnl/util/filesystem.py
index 380501a011..eb1df192b8 100644
--- a/lib/spack/spack/test/llnl/util/filesystem.py
+++ b/lib/spack/spack/test/llnl/util/filesystem.py
@@ -12,6 +12,7 @@ import sys
import pytest
import llnl.util.filesystem as fs
+from llnl.util.symlink import symlink
import spack.paths
@@ -36,9 +37,9 @@ def stage(tmpdir_factory):
fs.touchp('source/g/i/j/10')
# Create symlinks
- os.symlink(os.path.abspath('source/1'), 'source/2')
- os.symlink('b/2', 'source/a/b2')
- os.symlink('a/b', 'source/f')
+ symlink(os.path.abspath('source/1'), 'source/2')
+ symlink('b/2', 'source/a/b2')
+ symlink('a/b', 'source/f')
# Create destination directory
fs.mkdirp('dest')
@@ -174,15 +175,21 @@ class TestCopyTree:
fs.copy_tree('source', 'dest', symlinks=True)
assert os.path.exists('dest/2')
- assert os.path.islink('dest/2')
+ if sys.platform != "win32":
+ # TODO: islink will return false for junctions
+ assert os.path.islink('dest/2')
assert os.path.exists('dest/a/b2')
- with fs.working_dir('dest/a'):
- assert os.path.exists(os.readlink('b2'))
+ if sys.platform != "win32":
+ # TODO: Not supported for junctions ?
+ with fs.working_dir('dest/a'):
+ assert os.path.exists(os.readlink('b2'))
- assert (os.path.realpath('dest/f/2') ==
- os.path.abspath('dest/a/b/2'))
- assert os.path.realpath('dest/2') == os.path.abspath('dest/1')
+ if sys.platform != "win32":
+ # TODO: Not supported on Windows ?
+ assert (os.path.realpath('dest/f/2') ==
+ os.path.abspath('dest/a/b/2'))
+ assert os.path.realpath('dest/2') == os.path.abspath('dest/1')
def test_symlinks_true_ignore(self, stage):
"""Test copying when specifying relative paths that should be ignored
@@ -201,7 +208,8 @@ class TestCopyTree:
fs.copy_tree('source', 'dest', symlinks=False)
assert os.path.exists('dest/2')
- assert not os.path.islink('dest/2')
+ if sys.platform != "win32":
+ assert not os.path.islink('dest/2')
def test_glob_src(self, stage):
"""Test using a glob as the source."""
@@ -258,7 +266,8 @@ class TestInstallTree:
fs.install_tree('source', 'dest', symlinks=True)
assert os.path.exists('dest/2')
- assert os.path.islink('dest/2')
+ if sys.platform != "win32":
+ assert os.path.islink('dest/2')
check_added_exe_permissions('source/2', 'dest/2')
def test_symlinks_false(self, stage):
@@ -268,7 +277,8 @@ class TestInstallTree:
fs.install_tree('source', 'dest', symlinks=False)
assert os.path.exists('dest/2')
- assert not os.path.islink('dest/2')
+ if sys.platform != "win32":
+ assert not os.path.islink('dest/2')
check_added_exe_permissions('source/2', 'dest/2')
def test_glob_src(self, stage):
diff --git a/lib/spack/spack/test/llnl/util/link_tree.py b/lib/spack/spack/test/llnl/util/link_tree.py
index 7ff941221b..0b5bfb3b0c 100644
--- a/lib/spack/spack/test/llnl/util/link_tree.py
+++ b/lib/spack/spack/test/llnl/util/link_tree.py
@@ -9,6 +9,7 @@ import pytest
from llnl.util.filesystem import mkdirp, touchp, working_dir
from llnl.util.link_tree import LinkTree
+from llnl.util.symlink import islink
from spack.stage import Stage
@@ -42,7 +43,7 @@ def link_tree(stage):
def check_file_link(filename, expected_target):
assert os.path.isfile(filename)
- assert os.path.islink(filename)
+ assert islink(filename)
assert (os.path.abspath(os.path.realpath(filename)) ==
os.path.abspath(expected_target))
diff --git a/lib/spack/spack/test/packaging.py b/lib/spack/spack/test/packaging.py
index 1a83bd97e2..9d745d09db 100644
--- a/lib/spack/spack/test/packaging.py
+++ b/lib/spack/spack/test/packaging.py
@@ -16,6 +16,7 @@ import stat
import pytest
from llnl.util.filesystem import mkdirp
+from llnl.util.symlink import symlink
import spack.binary_distribution as bindist
import spack.cmd.buildcache as buildcache
@@ -79,7 +80,7 @@ echo $PATH"""
# Create an absolute symlink
linkname = os.path.join(spec.prefix, "link_to_dummy.txt")
- os.symlink(filename, linkname)
+ symlink(filename, linkname)
# Create the build cache and
# put it directly into the mirror
@@ -232,8 +233,8 @@ def test_relocate_links(tmpdir):
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)
+ symlink(old_binname, new_linkname)
+ symlink('/usr/lib/libc.so', new_linkname2)
relocate_links(filenames, old_layout_root,
old_install_prefix, new_install_prefix)
assert os.readlink(new_linkname) == new_binname
diff --git a/lib/spack/spack/test/verification.py b/lib/spack/spack/test/verification.py
index 30d4d376c0..c6063538c9 100644
--- a/lib/spack/spack/test/verification.py
+++ b/lib/spack/spack/test/verification.py
@@ -8,6 +8,7 @@ import os
import shutil
import llnl.util.filesystem as fs
+from llnl.util.symlink import symlink
import spack.spec
import spack.store
@@ -21,7 +22,7 @@ def test_link_manifest_entry(tmpdir):
file = str(tmpdir.join('file'))
open(file, 'a').close()
link = str(tmpdir.join('link'))
- os.symlink(file, link)
+ symlink(file, link)
data = spack.verify.create_manifest_entry(link)
assert data['type'] == 'link'
@@ -43,7 +44,7 @@ def test_link_manifest_entry(tmpdir):
file2 = str(tmpdir.join('file2'))
open(file2, 'a').close()
os.remove(link)
- os.symlink(file2, link)
+ symlink(file2, link)
results = spack.verify.check_entry(link, data)
assert results.has_errors()
@@ -157,7 +158,7 @@ def test_check_prefix_manifest(tmpdir):
f.write("I'm a little file short and stout")
link = os.path.join(bin_dir, 'run')
- os.symlink(file, link)
+ symlink(file, link)
spack.verify.write_manifest(spec)
results = spack.verify.check_spec_manifest(spec)