Allow environment views to be sym/hard link and copy types (#24832)

Add link type to spack.yaml format

Add tests to verify link behavior is correct for installed files
for all three view types

Co-authored-by: vsoch <vsoch@users.noreply.github.com>
This commit is contained in:
psakievich 2021-08-16 20:21:57 -06:00 committed by GitHub
parent 657a5c85cc
commit a81ec88c6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 16 deletions

View file

@ -42,12 +42,7 @@
import spack.schema.projections
import spack.store
from spack.config import validate
from spack.filesystem_view import (
YamlFilesystemView,
view_copy,
view_hardlink,
view_symlink,
)
from spack.filesystem_view import YamlFilesystemView, view_func_parser
from spack.util import spack_yaml as s_yaml
description = "project packages to a compact naming scheme on the filesystem."
@ -187,12 +182,10 @@ def view(parser, args):
ordered_projections = {}
# What method are we using for this view
if args.action in ("hardlink", "hard"):
link_fn = view_hardlink
elif args.action in ("copy", "relocate"):
link_fn = view_copy
if args.action in actions_link:
link_fn = view_func_parser(args.action)
else:
link_fn = view_symlink
link_fn = view_func_parser('symlink')
view = YamlFilesystemView(
path, spack.store.layout,

View file

@ -34,7 +34,11 @@
import spack.util.path
import spack.util.spack_json as sjson
import spack.util.spack_yaml as syaml
from spack.filesystem_view import YamlFilesystemView
from spack.filesystem_view import (
YamlFilesystemView,
inverse_view_func_parser,
view_func_parser,
)
from spack.spec import Spec
from spack.spec_list import InvalidSpecConstraintError, SpecList
from spack.util.path import substitute_path_variables
@ -456,12 +460,13 @@ def _eval_conditional(string):
class ViewDescriptor(object):
def __init__(self, base_path, root, projections={}, select=[], exclude=[],
link=default_view_link):
link=default_view_link, link_type='symlink'):
self.base = base_path
self.root = spack.util.path.canonicalize_path(root)
self.projections = projections
self.select = select
self.exclude = exclude
self.link_type = view_func_parser(link_type)
self.link = link
def select_fn(self, spec):
@ -475,7 +480,8 @@ def __eq__(self, other):
self.projections == other.projections,
self.select == other.select,
self.exclude == other.exclude,
self.link == other.link])
self.link == other.link,
self.link_type == other.link_type])
def to_dict(self):
ret = syaml.syaml_dict([('root', self.root)])
@ -490,6 +496,8 @@ def to_dict(self):
ret['select'] = self.select
if self.exclude:
ret['exclude'] = self.exclude
if self.link_type:
ret['link_type'] = inverse_view_func_parser(self.link_type)
if self.link != default_view_link:
ret['link'] = self.link
return ret
@ -501,7 +509,8 @@ def from_dict(base_path, d):
d.get('projections', {}),
d.get('select', []),
d.get('exclude', []),
d.get('link', default_view_link))
d.get('link', default_view_link),
d.get('link_type', 'symlink'))
@property
def _current_root(self):
@ -565,7 +574,8 @@ def view(self, new=None):
raise SpackEnvironmentViewError(msg)
return YamlFilesystemView(root, spack.store.layout,
ignore_conflicts=True,
projections=self.projections)
projections=self.projections,
link=self.link_type)
def __contains__(self, spec):
"""Is the spec described by the view descriptor

View file

@ -98,6 +98,29 @@ def view_copy(src, dst, view, spec=None):
)
def view_func_parser(parsed_name):
# What method are we using for this view
if parsed_name in ("hardlink", "hard"):
return view_hardlink
elif parsed_name in ("copy", "relocate"):
return view_copy
elif parsed_name in ("add", "symlink", "soft"):
return view_symlink
else:
raise ValueError("invalid link type for view: '%s'" % parsed_name)
def inverse_view_func_parser(view_type):
# get string based on view type
if view_type is view_hardlink:
link_name = 'hardlink'
elif view_type is view_copy:
link_name = 'copy'
else:
link_name = 'symlink'
return link_name
class FilesystemView(object):
"""
Governs a filesystem view that is located at certain root-directory.

View file

@ -126,6 +126,9 @@
'type': 'string',
'pattern': '(roots|all)',
},
'link_type': {
'type': 'string'
},
'select': {
'type': 'array',
'items': {

View file

@ -1948,6 +1948,35 @@ def test_view_link_roots(tmpdir, mock_fetch, mock_packages, mock_archive,
(spec.version, spec.compiler.name)))
@pytest.mark.parametrize('link_type', ['hardlink', 'copy', 'symlink'])
def test_view_link_type(link_type, tmpdir, mock_fetch, mock_packages, mock_archive,
install_mockery):
filename = str(tmpdir.join('spack.yaml'))
viewdir = str(tmpdir.join('view'))
with open(filename, 'w') as f:
f.write("""\
env:
specs:
- mpileaks
view:
default:
root: %s
link_type: %s""" % (viewdir, link_type))
with tmpdir.as_cwd():
env('create', 'test', './spack.yaml')
with ev.read('test'):
install()
test = ev.read('test')
for spec in test.roots():
file_path = test.default_view.view()._root
file_to_test = os.path.join(
file_path, spec.name)
assert os.path.isfile(file_to_test)
assert os.path.islink(file_to_test) == (link_type == 'symlink')
def test_view_link_all(tmpdir, mock_fetch, mock_packages, mock_archive,
install_mockery):
filename = str(tmpdir.join('spack.yaml'))