* Add pl2bat to PATH: Windows on Perl requires the script pl2bat.bat and Perl to be available to the installer via the PATH. The build and dependent environments of Perl on Windows have the install prefix bin added to the PATH. * symlink with win32file module instead of using Executable to call mklink (mklink is a shell function and so is not accessible in this manner).
113 lines
2.9 KiB
Python
113 lines
2.9 KiB
Python
# 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
|
|
|
|
from llnl.util import lang
|
|
|
|
is_windows = _platform == 'win32'
|
|
|
|
if is_windows:
|
|
from win32file import CreateHardLink
|
|
|
|
|
|
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)
|
|
|
|
CreateHardLink(link, path)
|
|
|
|
|
|
@lang.memoized
|
|
def _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)
|
|
|
|
return can_symlink_directories and can_symlink_files
|
|
|
|
|
|
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
|