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
@ -42,12 +42,7 @@
|
|||||||
import spack.schema.projections
|
import spack.schema.projections
|
||||||
import spack.store
|
import spack.store
|
||||||
from spack.config import validate
|
from spack.config import validate
|
||||||
from spack.filesystem_view import (
|
from spack.filesystem_view import YamlFilesystemView, view_func_parser
|
||||||
YamlFilesystemView,
|
|
||||||
view_copy,
|
|
||||||
view_hardlink,
|
|
||||||
view_symlink,
|
|
||||||
)
|
|
||||||
from spack.util import spack_yaml as s_yaml
|
from spack.util import spack_yaml as s_yaml
|
||||||
|
|
||||||
description = "project packages to a compact naming scheme on the filesystem."
|
description = "project packages to a compact naming scheme on the filesystem."
|
||||||
@ -187,12 +182,10 @@ def view(parser, args):
|
|||||||
ordered_projections = {}
|
ordered_projections = {}
|
||||||
|
|
||||||
# What method are we using for this view
|
# What method are we using for this view
|
||||||
if args.action in ("hardlink", "hard"):
|
if args.action in actions_link:
|
||||||
link_fn = view_hardlink
|
link_fn = view_func_parser(args.action)
|
||||||
elif args.action in ("copy", "relocate"):
|
|
||||||
link_fn = view_copy
|
|
||||||
else:
|
else:
|
||||||
link_fn = view_symlink
|
link_fn = view_func_parser('symlink')
|
||||||
|
|
||||||
view = YamlFilesystemView(
|
view = YamlFilesystemView(
|
||||||
path, spack.store.layout,
|
path, spack.store.layout,
|
||||||
|
@ -34,7 +34,11 @@
|
|||||||
import spack.util.path
|
import spack.util.path
|
||||||
import spack.util.spack_json as sjson
|
import spack.util.spack_json as sjson
|
||||||
import spack.util.spack_yaml as syaml
|
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 import Spec
|
||||||
from spack.spec_list import InvalidSpecConstraintError, SpecList
|
from spack.spec_list import InvalidSpecConstraintError, SpecList
|
||||||
from spack.util.path import substitute_path_variables
|
from spack.util.path import substitute_path_variables
|
||||||
@ -456,12 +460,13 @@ def _eval_conditional(string):
|
|||||||
|
|
||||||
class ViewDescriptor(object):
|
class ViewDescriptor(object):
|
||||||
def __init__(self, base_path, root, projections={}, select=[], exclude=[],
|
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.base = base_path
|
||||||
self.root = spack.util.path.canonicalize_path(root)
|
self.root = spack.util.path.canonicalize_path(root)
|
||||||
self.projections = projections
|
self.projections = projections
|
||||||
self.select = select
|
self.select = select
|
||||||
self.exclude = exclude
|
self.exclude = exclude
|
||||||
|
self.link_type = view_func_parser(link_type)
|
||||||
self.link = link
|
self.link = link
|
||||||
|
|
||||||
def select_fn(self, spec):
|
def select_fn(self, spec):
|
||||||
@ -475,7 +480,8 @@ def __eq__(self, other):
|
|||||||
self.projections == other.projections,
|
self.projections == other.projections,
|
||||||
self.select == other.select,
|
self.select == other.select,
|
||||||
self.exclude == other.exclude,
|
self.exclude == other.exclude,
|
||||||
self.link == other.link])
|
self.link == other.link,
|
||||||
|
self.link_type == other.link_type])
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
ret = syaml.syaml_dict([('root', self.root)])
|
ret = syaml.syaml_dict([('root', self.root)])
|
||||||
@ -490,6 +496,8 @@ def to_dict(self):
|
|||||||
ret['select'] = self.select
|
ret['select'] = self.select
|
||||||
if self.exclude:
|
if self.exclude:
|
||||||
ret['exclude'] = 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:
|
if self.link != default_view_link:
|
||||||
ret['link'] = self.link
|
ret['link'] = self.link
|
||||||
return ret
|
return ret
|
||||||
@ -501,7 +509,8 @@ def from_dict(base_path, d):
|
|||||||
d.get('projections', {}),
|
d.get('projections', {}),
|
||||||
d.get('select', []),
|
d.get('select', []),
|
||||||
d.get('exclude', []),
|
d.get('exclude', []),
|
||||||
d.get('link', default_view_link))
|
d.get('link', default_view_link),
|
||||||
|
d.get('link_type', 'symlink'))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _current_root(self):
|
def _current_root(self):
|
||||||
@ -565,7 +574,8 @@ def view(self, new=None):
|
|||||||
raise SpackEnvironmentViewError(msg)
|
raise SpackEnvironmentViewError(msg)
|
||||||
return YamlFilesystemView(root, spack.store.layout,
|
return YamlFilesystemView(root, spack.store.layout,
|
||||||
ignore_conflicts=True,
|
ignore_conflicts=True,
|
||||||
projections=self.projections)
|
projections=self.projections,
|
||||||
|
link=self.link_type)
|
||||||
|
|
||||||
def __contains__(self, spec):
|
def __contains__(self, spec):
|
||||||
"""Is the spec described by the view descriptor
|
"""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):
|
class FilesystemView(object):
|
||||||
"""
|
"""
|
||||||
Governs a filesystem view that is located at certain root-directory.
|
Governs a filesystem view that is located at certain root-directory.
|
||||||
|
@ -126,6 +126,9 @@
|
|||||||
'type': 'string',
|
'type': 'string',
|
||||||
'pattern': '(roots|all)',
|
'pattern': '(roots|all)',
|
||||||
},
|
},
|
||||||
|
'link_type': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
'select': {
|
'select': {
|
||||||
'type': 'array',
|
'type': 'array',
|
||||||
'items': {
|
'items': {
|
||||||
|
@ -1948,6 +1948,35 @@ def test_view_link_roots(tmpdir, mock_fetch, mock_packages, mock_archive,
|
|||||||
(spec.version, spec.compiler.name)))
|
(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,
|
def test_view_link_all(tmpdir, mock_fetch, mock_packages, mock_archive,
|
||||||
install_mockery):
|
install_mockery):
|
||||||
filename = str(tmpdir.join('spack.yaml'))
|
filename = str(tmpdir.join('spack.yaml'))
|
||||||
|
Loading…
Reference in New Issue
Block a user