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:
parent
657a5c85cc
commit
a81ec88c6c
5 changed files with 74 additions and 16 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -126,6 +126,9 @@
|
|||
'type': 'string',
|
||||
'pattern': '(roots|all)',
|
||||
},
|
||||
'link_type': {
|
||||
'type': 'string'
|
||||
},
|
||||
'select': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
|
|
|
@ -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'))
|
||||
|
|
Loading…
Reference in a new issue