stacks: update view management for multiple/combinatorial views
This adds notion of a default view, other views in environments
This commit is contained in:
		
				
					committed by
					
						
						Todd Gamblin
					
				
			
			
				
	
			
			
			
						parent
						
							d0bfe0d6a8
						
					
				
				
					commit
					cebf1fd668
				
			@@ -315,11 +315,11 @@ def env_view(args):
 | 
			
		||||
            if args.view_path:
 | 
			
		||||
                view_path = args.view_path
 | 
			
		||||
            else:
 | 
			
		||||
                view_path = env.default_view_path
 | 
			
		||||
            env.update_view(view_path)
 | 
			
		||||
                view_path = env.create_view_path
 | 
			
		||||
            env.update_default_view(view_path)
 | 
			
		||||
            env.write()
 | 
			
		||||
        elif args.action == ViewAction.disable:
 | 
			
		||||
            env.update_view(None)
 | 
			
		||||
            env.update_default_view(None)
 | 
			
		||||
            env.write()
 | 
			
		||||
    else:
 | 
			
		||||
        tty.msg("No active environment")
 | 
			
		||||
 
 | 
			
		||||
@@ -141,7 +141,7 @@ def activate(
 | 
			
		||||
            cmds += 'export SPACK_OLD_PS1="${PS1}"; fi;\n'
 | 
			
		||||
            cmds += 'export PS1="%s ${PS1}";\n' % prompt
 | 
			
		||||
 | 
			
		||||
    if add_view and env._view_path:
 | 
			
		||||
    if add_view and env.default_view_path:
 | 
			
		||||
        cmds += env.add_view_to_shell(shell)
 | 
			
		||||
 | 
			
		||||
    return cmds
 | 
			
		||||
@@ -183,7 +183,7 @@ def deactivate(shell='sh'):
 | 
			
		||||
        cmds += 'unset SPACK_OLD_PS1; export SPACK_OLD_PS1;\n'
 | 
			
		||||
        cmds += 'fi;\n'
 | 
			
		||||
 | 
			
		||||
    if _active_environment._view_path:
 | 
			
		||||
    if _active_environment.default_view_path:
 | 
			
		||||
        cmds += _active_environment.rm_view_from_shell(shell)
 | 
			
		||||
 | 
			
		||||
    tty.debug("Deactivated environmennt '%s'" % _active_environment.name)
 | 
			
		||||
@@ -467,9 +467,9 @@ def __init__(self, path, init_file=None, with_view=None):
 | 
			
		||||
                self._read_manifest(default_manifest_yaml)
 | 
			
		||||
 | 
			
		||||
        if with_view is False:
 | 
			
		||||
            self._view_path = None
 | 
			
		||||
            self.views = None
 | 
			
		||||
        elif isinstance(with_view, six.string_types):
 | 
			
		||||
            self._view_path = with_view
 | 
			
		||||
            self.views = {'default': self.create_view_descriptor(with_view)}
 | 
			
		||||
        # If with_view is None, then defer to the view settings determined by
 | 
			
		||||
        # the manifest file
 | 
			
		||||
 | 
			
		||||
@@ -499,27 +499,18 @@ def _read_manifest(self, f):
 | 
			
		||||
        enable_view = config_dict(self.yaml).get('view')
 | 
			
		||||
        # enable_view can be boolean, string, or None
 | 
			
		||||
        if enable_view is True or enable_view is None:
 | 
			
		||||
            self._view_path = self.default_view_path
 | 
			
		||||
            self.views = {'default': self.create_view_descriptor()}
 | 
			
		||||
        elif isinstance(enable_view, six.string_types):
 | 
			
		||||
            self._view_path = enable_view
 | 
			
		||||
            self.views = {'default': self.create_view_descriptor(enable_view)}
 | 
			
		||||
        elif enable_view:
 | 
			
		||||
            self.views = enable_view
 | 
			
		||||
        else:
 | 
			
		||||
            self._view_path = None
 | 
			
		||||
            self.views = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def user_specs(self):
 | 
			
		||||
        return self.read_specs['specs']
 | 
			
		||||
 | 
			
		||||
        enable_view = config_dict(self.yaml).get('view')
 | 
			
		||||
        # enable_view can be true/false, a string, or None (if the manifest did
 | 
			
		||||
        # not specify it)
 | 
			
		||||
        if enable_view is True or enable_view is None:
 | 
			
		||||
            self._view_path = self.default_view_path
 | 
			
		||||
        elif isinstance(enable_view, six.string_types):
 | 
			
		||||
            self._view_path = enable_view
 | 
			
		||||
        else:
 | 
			
		||||
            # enable_view is False
 | 
			
		||||
            self._view_path = None
 | 
			
		||||
 | 
			
		||||
    def _set_user_specs_from_lockfile(self):
 | 
			
		||||
        """Copy user_specs from a read-in lockfile."""
 | 
			
		||||
        self.read_specs = {
 | 
			
		||||
@@ -582,8 +573,13 @@ def repos_path(self):
 | 
			
		||||
    def log_path(self):
 | 
			
		||||
        return os.path.join(self.path, env_subdir_name, 'logs')
 | 
			
		||||
 | 
			
		||||
    def create_view_descriptor(self, root=None):
 | 
			
		||||
        if root is None:
 | 
			
		||||
            root = self.create_view_path
 | 
			
		||||
        return {'root': root, 'projections': {}}
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def default_view_path(self):
 | 
			
		||||
    def create_view_path(self):
 | 
			
		||||
        return os.path.join(self.env_subdir_path, 'view')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
@@ -824,26 +820,62 @@ def _install(self, spec, **install_args):
 | 
			
		||||
                os.remove(build_log_link)
 | 
			
		||||
            os.symlink(spec.package.build_log_path, build_log_link)
 | 
			
		||||
 | 
			
		||||
    def view(self):
 | 
			
		||||
        if not self._view_path:
 | 
			
		||||
    @property
 | 
			
		||||
    def all_views(self):
 | 
			
		||||
        if not self.views:
 | 
			
		||||
            raise SpackEnvironmentError(
 | 
			
		||||
                "{0} does not have a view enabled".format(self.name))
 | 
			
		||||
 | 
			
		||||
        return YamlFilesystemView(
 | 
			
		||||
            self._view_path, spack.store.layout, ignore_conflicts=True)
 | 
			
		||||
        return [YamlFilesystemView(view['root'], spack.store.layout,
 | 
			
		||||
                                   ignore_conflicts=True,
 | 
			
		||||
                                   projections=view['projections'])
 | 
			
		||||
                for view in self.views.values()]
 | 
			
		||||
 | 
			
		||||
    def update_view(self, view_path):
 | 
			
		||||
        if self._view_path and self._view_path != view_path:
 | 
			
		||||
            shutil.rmtree(self._view_path)
 | 
			
		||||
    @property
 | 
			
		||||
    def default_view(self):
 | 
			
		||||
        if not self.views:
 | 
			
		||||
            raise SpackEnvironmentError(
 | 
			
		||||
                "{0} does not have a view enabled".format(self.name))
 | 
			
		||||
 | 
			
		||||
        self._view_path = view_path
 | 
			
		||||
        if 'default' not in self.views:
 | 
			
		||||
            raise SpackEnvironmentError(
 | 
			
		||||
                "{0} does not have a default view enabled".format(self.name))
 | 
			
		||||
 | 
			
		||||
    def regenerate_view(self):
 | 
			
		||||
        if not self._view_path:
 | 
			
		||||
        return YamlFilesystemView(self.default_view_path,
 | 
			
		||||
                                  spack.store.layout, ignore_confligs=True)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def default_view_path(self):
 | 
			
		||||
        if not self.views or 'default' not in self.views:
 | 
			
		||||
            return None
 | 
			
		||||
        return self.views['default']['root']
 | 
			
		||||
 | 
			
		||||
    def update_default_view(self, viewpath):
 | 
			
		||||
        if self.default_view_path and self.default_view_path != viewpath:
 | 
			
		||||
            shutil.rmtree(self.default_view_path)
 | 
			
		||||
 | 
			
		||||
        if viewpath:
 | 
			
		||||
            if self.default_view_path:
 | 
			
		||||
                self.views['default']['root'] = viewpath
 | 
			
		||||
            elif self.views:
 | 
			
		||||
                self.views['default'] = self.create_view_descriptor(viewpath)
 | 
			
		||||
            else:
 | 
			
		||||
                self.views = {'default': self.create_view_descriptor(viewpath)}
 | 
			
		||||
        else:
 | 
			
		||||
            self.views.pop('default', None)
 | 
			
		||||
 | 
			
		||||
    def regenerate_views(self):
 | 
			
		||||
        if not self.views:
 | 
			
		||||
            tty.debug("Skip view update, this environment does not"
 | 
			
		||||
                      " maintain a view")
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        for name, view in zip(self.views.keys(), self.all_views):
 | 
			
		||||
            self.regenerate_view(name, view)
 | 
			
		||||
 | 
			
		||||
    def regenerate_view(self, name, view):
 | 
			
		||||
        view_descriptor = self.views[name]
 | 
			
		||||
 | 
			
		||||
        specs_for_view = []
 | 
			
		||||
        for spec in self._get_environment_specs():
 | 
			
		||||
            # The view does not store build deps, so if we want it to
 | 
			
		||||
@@ -852,13 +884,23 @@ def regenerate_view(self):
 | 
			
		||||
            specs_for_view.append(spack.spec.Spec.from_dict(
 | 
			
		||||
                spec.to_dict(all_deps=False)
 | 
			
		||||
            ))
 | 
			
		||||
 | 
			
		||||
        select = view_descriptor.get('select', [])
 | 
			
		||||
        if select:
 | 
			
		||||
            select_fn = lambda x: any(x.satisfies(s) for s in select)
 | 
			
		||||
            specs_for_view = list(filter(select_fn, specs_for_view))
 | 
			
		||||
 | 
			
		||||
        exclude = view_descriptor.get('exclude', [])
 | 
			
		||||
        if exclude:
 | 
			
		||||
            exclude_fn = lambda x: not any(x.satisfies(e) for e in exclude)
 | 
			
		||||
            specs_for_view = list(filter(exclude_fn, specs_for_view))
 | 
			
		||||
 | 
			
		||||
        installed_specs_for_view = set(s for s in specs_for_view
 | 
			
		||||
                                       if s.package.installed)
 | 
			
		||||
 | 
			
		||||
        view = self.view()
 | 
			
		||||
        view.clean()
 | 
			
		||||
        specs_in_view = set(view.get_all_specs())
 | 
			
		||||
        tty.msg("Updating view at {0}".format(self._view_path))
 | 
			
		||||
        tty.msg("Updating view at {0}".format(view_descriptor['root']))
 | 
			
		||||
 | 
			
		||||
        rm_specs = specs_in_view - installed_specs_for_view
 | 
			
		||||
        view.remove_specs(*rm_specs, with_dependents=False)
 | 
			
		||||
@@ -880,7 +922,7 @@ def _shell_vars(self):
 | 
			
		||||
        path_updates = list()
 | 
			
		||||
        for var, subdirs in updates:
 | 
			
		||||
            paths = filter(lambda x: os.path.exists(x),
 | 
			
		||||
                           list(os.path.join(self._view_path, x)
 | 
			
		||||
                           list(os.path.join(self.default_view_path, x)
 | 
			
		||||
                                for x in subdirs))
 | 
			
		||||
            path_updates.append((var, paths))
 | 
			
		||||
        return path_updates
 | 
			
		||||
@@ -945,7 +987,7 @@ def install_all(self, args=None):
 | 
			
		||||
                    os.remove(build_log_link)
 | 
			
		||||
                os.symlink(spec.package.build_log_path, build_log_link)
 | 
			
		||||
 | 
			
		||||
        self.regenerate_view()
 | 
			
		||||
        self.regenerate_views()
 | 
			
		||||
 | 
			
		||||
    def all_specs_by_hash(self):
 | 
			
		||||
        """Map of hashes to spec for all specs in this environment."""
 | 
			
		||||
@@ -1014,8 +1056,7 @@ def _get_environment_specs(self, recurse_dependencies=True):
 | 
			
		||||
        If these specs appear under different user_specs, only one copy
 | 
			
		||||
        is added to the list returned.
 | 
			
		||||
        """
 | 
			
		||||
        package_to_spec = {}
 | 
			
		||||
        spec_list = list()
 | 
			
		||||
        spec_set = set()
 | 
			
		||||
 | 
			
		||||
        for spec_hash in self.concretized_order:
 | 
			
		||||
            spec = self.specs_by_hash[spec_hash]
 | 
			
		||||
@@ -1023,17 +1064,9 @@ def _get_environment_specs(self, recurse_dependencies=True):
 | 
			
		||||
            specs = (spec.traverse(deptype=('link', 'run'))
 | 
			
		||||
                     if recurse_dependencies else (spec,))
 | 
			
		||||
 | 
			
		||||
            for dep in specs:
 | 
			
		||||
                prior = package_to_spec.get(dep.name)
 | 
			
		||||
                if prior and prior != dep:
 | 
			
		||||
                    tty.debug("{0} takes priority over {1}"
 | 
			
		||||
                              .format(package_to_spec[dep.name].format(),
 | 
			
		||||
                                      dep.format()))
 | 
			
		||||
                else:
 | 
			
		||||
                    package_to_spec[dep.name] = dep
 | 
			
		||||
                    spec_list.append(dep)
 | 
			
		||||
            spec_set.update(specs)
 | 
			
		||||
 | 
			
		||||
        return spec_list
 | 
			
		||||
        return list(spec_set)
 | 
			
		||||
 | 
			
		||||
    def _to_lockfile_dict(self):
 | 
			
		||||
        """Create a dictionary to store a lockfile for this environment."""
 | 
			
		||||
@@ -1158,10 +1191,16 @@ def write(self):
 | 
			
		||||
        yaml_spec_list = config_dict(self.yaml).setdefault('specs', [])
 | 
			
		||||
        yaml_spec_list[:] = self.user_specs.yaml_list
 | 
			
		||||
 | 
			
		||||
        if self._view_path == self.default_view_path:
 | 
			
		||||
            view = True
 | 
			
		||||
        elif self._view_path:
 | 
			
		||||
            view = self._view_path
 | 
			
		||||
        if self.views and len(self.views) == 1 and self.default_view_path:
 | 
			
		||||
            path = self.default_view_path
 | 
			
		||||
            if self.views['default'] == self.create_view_descriptor():
 | 
			
		||||
                view = True
 | 
			
		||||
            elif self.views['default'] == self.create_view_descriptor(path):
 | 
			
		||||
                view = path
 | 
			
		||||
            else:
 | 
			
		||||
                view = self.views
 | 
			
		||||
        elif self.views:
 | 
			
		||||
            view = self.views
 | 
			
		||||
        else:
 | 
			
		||||
            view = False
 | 
			
		||||
 | 
			
		||||
@@ -1179,7 +1218,7 @@ def write(self):
 | 
			
		||||
 | 
			
		||||
        # TODO: for operations that just add to the env (install etc.) this
 | 
			
		||||
        # could just call update_view
 | 
			
		||||
        self.regenerate_view()
 | 
			
		||||
        self.regenerate_views()
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        self._previous_active = _active_environment
 | 
			
		||||
 
 | 
			
		||||
@@ -210,9 +210,16 @@ def __init__(self, root, layout, **kwargs):
 | 
			
		||||
            with open(projections_path, 'w') as f:
 | 
			
		||||
                f.write(s_yaml.dump({'projections': self.projections}))
 | 
			
		||||
        else:
 | 
			
		||||
            msg = 'View at %s has projections file' % self._root
 | 
			
		||||
            msg += ' and was passed projections manually.'
 | 
			
		||||
            raise ConflictingProjectionsError(msg)
 | 
			
		||||
            # Ensure projections are the same from each source
 | 
			
		||||
            # Read projections file from view
 | 
			
		||||
            with open(projections_path, 'r') as f:
 | 
			
		||||
                projections_data = s_yaml.load(f)
 | 
			
		||||
                spack.config.validate(projections_data,
 | 
			
		||||
                                      spack.schema.projections.schema)
 | 
			
		||||
                if self.projections != projections_data['projections']:
 | 
			
		||||
                    msg = 'View at %s has projections file' % self._root
 | 
			
		||||
                    msg += ' which does not match projections passed manually.'
 | 
			
		||||
                    raise ConflictingProjectionsError(msg)
 | 
			
		||||
 | 
			
		||||
        self.extensions_layout = YamlViewExtensionsLayout(self, layout)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -67,9 +67,6 @@
 | 
			
		||||
                            'type': 'string'
 | 
			
		||||
                        },
 | 
			
		||||
                    },
 | 
			
		||||
                    'view': {
 | 
			
		||||
                        'type': ['boolean', 'string']
 | 
			
		||||
                    },
 | 
			
		||||
                    'definitions': {
 | 
			
		||||
                        'type': 'array',
 | 
			
		||||
                        'default': [],
 | 
			
		||||
@@ -81,7 +78,7 @@
 | 
			
		||||
                                }
 | 
			
		||||
                            },
 | 
			
		||||
                            'patternProperties': {
 | 
			
		||||
                                '^(?!when$)\w*': spec_list_schema
 | 
			
		||||
                               r'^(?!when$)\w*': spec_list_schema
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
@@ -91,25 +88,29 @@
 | 
			
		||||
                            {'type': 'boolean'},
 | 
			
		||||
                            {'type': 'string'},
 | 
			
		||||
                            {'type': 'object',
 | 
			
		||||
                             'required': ['root'],
 | 
			
		||||
                             'additionalProperties': False,
 | 
			
		||||
                             'properties': {
 | 
			
		||||
                                 'root': {
 | 
			
		||||
                                     'type': 'string'
 | 
			
		||||
                                 },
 | 
			
		||||
                                 'select': {
 | 
			
		||||
                                     'type': 'array',
 | 
			
		||||
                                     'items': {
 | 
			
		||||
                                         'type': 'string'
 | 
			
		||||
                                     }
 | 
			
		||||
                                 },
 | 
			
		||||
                                 'exclude': {
 | 
			
		||||
                                     'type': 'array',
 | 
			
		||||
                                     'items': {
 | 
			
		||||
                                         'type': 'string'
 | 
			
		||||
                                     }
 | 
			
		||||
                                 },
 | 
			
		||||
                                 'projections': projections_scheme
 | 
			
		||||
                             'patternProperties': {
 | 
			
		||||
                                  r'\w+': {
 | 
			
		||||
                                      'required': ['root'],
 | 
			
		||||
                                      'additionalProperties': False,
 | 
			
		||||
                                      'properties': {
 | 
			
		||||
                                          'root': {
 | 
			
		||||
                                              'type': 'string'
 | 
			
		||||
                                          },
 | 
			
		||||
                                          'select': {
 | 
			
		||||
                                              'type': 'array',
 | 
			
		||||
                                              'items': {
 | 
			
		||||
                                                  'type': 'string'
 | 
			
		||||
                                              }
 | 
			
		||||
                                          },
 | 
			
		||||
                                          'exclude': {
 | 
			
		||||
                                              'type': 'array',
 | 
			
		||||
                                              'items': {
 | 
			
		||||
                                                  'type': 'string'
 | 
			
		||||
                                              }
 | 
			
		||||
                                        },
 | 
			
		||||
                                          'projections': projections_scheme
 | 
			
		||||
                                      }
 | 
			
		||||
                                  }
 | 
			
		||||
                             }
 | 
			
		||||
                            }
 | 
			
		||||
                        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,13 @@ def check_viewdir_removal(viewdir):
 | 
			
		||||
            os.listdir(str(viewdir.join('.spack'))) == ['projections.yaml'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture()
 | 
			
		||||
def env_deactivate():
 | 
			
		||||
    yield
 | 
			
		||||
    spack.environment._active_environment = None
 | 
			
		||||
    os.environ.pop('SPACK_ENV', None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_add():
 | 
			
		||||
    e = ev.create('test')
 | 
			
		||||
    e.add('mpileaks')
 | 
			
		||||
@@ -637,7 +644,7 @@ def test_env_without_view_install(
 | 
			
		||||
 | 
			
		||||
    test_env = ev.read('test')
 | 
			
		||||
    with pytest.raises(spack.environment.SpackEnvironmentError):
 | 
			
		||||
        test_env.view()
 | 
			
		||||
        test_env.default_view
 | 
			
		||||
 | 
			
		||||
    view_dir = tmpdir.mkdir('view')
 | 
			
		||||
 | 
			
		||||
@@ -668,7 +675,7 @@ def test_env_config_view_default(
 | 
			
		||||
 | 
			
		||||
    e = ev.read('test')
 | 
			
		||||
    # Try retrieving the view object
 | 
			
		||||
    view = e.view()
 | 
			
		||||
    view = e.default_view
 | 
			
		||||
    assert view.get_spec('mpileaks')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1088,3 +1095,259 @@ def test_stack_definition_conditional_add_write(tmpdir):
 | 
			
		||||
        assert 'callpath' in packages_lists[1]['packages']
 | 
			
		||||
        assert 'zmpi' in packages_lists[0]['packages']
 | 
			
		||||
        assert 'zmpi' not in packages_lists[1]['packages']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stack_combinatorial_view(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:
 | 
			
		||||
  definitions:
 | 
			
		||||
    - packages: [mpileaks, callpath]
 | 
			
		||||
    - compilers: ['%%gcc', '%%clang']
 | 
			
		||||
  specs:
 | 
			
		||||
    - matrix:
 | 
			
		||||
        - [$packages]
 | 
			
		||||
        - [$compilers]
 | 
			
		||||
 | 
			
		||||
  view:
 | 
			
		||||
    combinatorial:
 | 
			
		||||
      root: %s
 | 
			
		||||
      projections:
 | 
			
		||||
        'all': '${package}/${version}-${compilername}'""" % viewdir)
 | 
			
		||||
    with tmpdir.as_cwd():
 | 
			
		||||
        env('create', 'test', './spack.yaml')
 | 
			
		||||
        with ev.read('test'):
 | 
			
		||||
            install()
 | 
			
		||||
 | 
			
		||||
        test = ev.read('test')
 | 
			
		||||
        for _, spec in test.concretized_specs():
 | 
			
		||||
            assert os.path.exists(
 | 
			
		||||
                os.path.join(viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                             (spec.version, spec.compiler.name)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stack_view_select(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:
 | 
			
		||||
  definitions:
 | 
			
		||||
    - packages: [mpileaks, callpath]
 | 
			
		||||
    - compilers: ['%%gcc', '%%clang']
 | 
			
		||||
  specs:
 | 
			
		||||
    - matrix:
 | 
			
		||||
        - [$packages]
 | 
			
		||||
        - [$compilers]
 | 
			
		||||
 | 
			
		||||
  view:
 | 
			
		||||
    combinatorial:
 | 
			
		||||
      root: %s
 | 
			
		||||
      select: ['%%gcc']
 | 
			
		||||
      projections:
 | 
			
		||||
        'all': '${package}/${version}-${compilername}'""" % viewdir)
 | 
			
		||||
    with tmpdir.as_cwd():
 | 
			
		||||
        env('create', 'test', './spack.yaml')
 | 
			
		||||
        with ev.read('test'):
 | 
			
		||||
            install()
 | 
			
		||||
 | 
			
		||||
        test = ev.read('test')
 | 
			
		||||
        for _, spec in test.concretized_specs():
 | 
			
		||||
            if spec.satisfies('%gcc'):
 | 
			
		||||
                assert os.path.exists(
 | 
			
		||||
                    os.path.join(viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                                 (spec.version, spec.compiler.name)))
 | 
			
		||||
            else:
 | 
			
		||||
                assert not os.path.exists(
 | 
			
		||||
                    os.path.join(viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                                 (spec.version, spec.compiler.name)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stack_view_exclude(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:
 | 
			
		||||
  definitions:
 | 
			
		||||
    - packages: [mpileaks, callpath]
 | 
			
		||||
    - compilers: ['%%gcc', '%%clang']
 | 
			
		||||
  specs:
 | 
			
		||||
    - matrix:
 | 
			
		||||
        - [$packages]
 | 
			
		||||
        - [$compilers]
 | 
			
		||||
 | 
			
		||||
  view:
 | 
			
		||||
    combinatorial:
 | 
			
		||||
      root: %s
 | 
			
		||||
      exclude: [callpath]
 | 
			
		||||
      projections:
 | 
			
		||||
        'all': '${package}/${version}-${compilername}'""" % viewdir)
 | 
			
		||||
    with tmpdir.as_cwd():
 | 
			
		||||
        env('create', 'test', './spack.yaml')
 | 
			
		||||
        with ev.read('test'):
 | 
			
		||||
            install()
 | 
			
		||||
 | 
			
		||||
        test = ev.read('test')
 | 
			
		||||
        for _, spec in test.concretized_specs():
 | 
			
		||||
            if not spec.satisfies('callpath'):
 | 
			
		||||
                assert os.path.exists(
 | 
			
		||||
                    os.path.join(viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                                 (spec.version, spec.compiler.name)))
 | 
			
		||||
            else:
 | 
			
		||||
                assert not os.path.exists(
 | 
			
		||||
                    os.path.join(viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                                 (spec.version, spec.compiler.name)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stack_view_select_and_exclude(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:
 | 
			
		||||
  definitions:
 | 
			
		||||
    - packages: [mpileaks, callpath]
 | 
			
		||||
    - compilers: ['%%gcc', '%%clang']
 | 
			
		||||
  specs:
 | 
			
		||||
    - matrix:
 | 
			
		||||
        - [$packages]
 | 
			
		||||
        - [$compilers]
 | 
			
		||||
 | 
			
		||||
  view:
 | 
			
		||||
    combinatorial:
 | 
			
		||||
      root: %s
 | 
			
		||||
      select: ['%%gcc']
 | 
			
		||||
      exclude: [callpath]
 | 
			
		||||
      projections:
 | 
			
		||||
        'all': '${package}/${version}-${compilername}'""" % viewdir)
 | 
			
		||||
    with tmpdir.as_cwd():
 | 
			
		||||
        env('create', 'test', './spack.yaml')
 | 
			
		||||
        with ev.read('test'):
 | 
			
		||||
            install()
 | 
			
		||||
 | 
			
		||||
        test = ev.read('test')
 | 
			
		||||
        for _, spec in test.concretized_specs():
 | 
			
		||||
            if spec.satisfies('%gcc') and not spec.satisfies('callpath'):
 | 
			
		||||
                assert os.path.exists(
 | 
			
		||||
                    os.path.join(viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                                 (spec.version, spec.compiler.name)))
 | 
			
		||||
            else:
 | 
			
		||||
                assert not os.path.exists(
 | 
			
		||||
                    os.path.join(viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                                 (spec.version, spec.compiler.name)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stack_view_activate_from_default(tmpdir, mock_fetch, mock_packages,
 | 
			
		||||
                                          mock_archive, install_mockery,
 | 
			
		||||
                                          env_deactivate):
 | 
			
		||||
    filename = str(tmpdir.join('spack.yaml'))
 | 
			
		||||
    viewdir = str(tmpdir.join('view'))
 | 
			
		||||
    with open(filename, 'w') as f:
 | 
			
		||||
        f.write("""\
 | 
			
		||||
env:
 | 
			
		||||
  definitions:
 | 
			
		||||
    - packages: [mpileaks, cmake]
 | 
			
		||||
    - compilers: ['%%gcc', '%%clang']
 | 
			
		||||
  specs:
 | 
			
		||||
    - matrix:
 | 
			
		||||
        - [$packages]
 | 
			
		||||
        - [$compilers]
 | 
			
		||||
 | 
			
		||||
  view:
 | 
			
		||||
    default:
 | 
			
		||||
      root: %s
 | 
			
		||||
      select: ['%%gcc']""" % viewdir)
 | 
			
		||||
    with tmpdir.as_cwd():
 | 
			
		||||
        env('create', 'test', './spack.yaml')
 | 
			
		||||
        with ev.read('test'):
 | 
			
		||||
            install()
 | 
			
		||||
 | 
			
		||||
        shell = env('activate', '--sh', 'test')
 | 
			
		||||
        assert 'PATH' in shell
 | 
			
		||||
        assert os.path.join(viewdir, 'bin') in shell
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stack_view_no_activate_without_default(tmpdir, mock_fetch,
 | 
			
		||||
                                                mock_packages, mock_archive,
 | 
			
		||||
                                                install_mockery,
 | 
			
		||||
                                                env_deactivate):
 | 
			
		||||
    filename = str(tmpdir.join('spack.yaml'))
 | 
			
		||||
    viewdir = str(tmpdir.join('view'))
 | 
			
		||||
    with open(filename, 'w') as f:
 | 
			
		||||
        f.write("""\
 | 
			
		||||
env:
 | 
			
		||||
  definitions:
 | 
			
		||||
    - packages: [mpileaks, cmake]
 | 
			
		||||
    - compilers: ['%%gcc', '%%clang']
 | 
			
		||||
  specs:
 | 
			
		||||
    - matrix:
 | 
			
		||||
        - [$packages]
 | 
			
		||||
        - [$compilers]
 | 
			
		||||
 | 
			
		||||
  view:
 | 
			
		||||
    not-default:
 | 
			
		||||
      root: %s
 | 
			
		||||
      select: ['%%gcc']""" % viewdir)
 | 
			
		||||
    with tmpdir.as_cwd():
 | 
			
		||||
        env('create', 'test', './spack.yaml')
 | 
			
		||||
        with ev.read('test'):
 | 
			
		||||
            install()
 | 
			
		||||
 | 
			
		||||
        shell = env('activate', '--sh', 'test')
 | 
			
		||||
        assert 'PATH' not in shell
 | 
			
		||||
        assert viewdir not in shell
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stack_view_multiple_views(tmpdir, mock_fetch, mock_packages,
 | 
			
		||||
                                   mock_archive, install_mockery,
 | 
			
		||||
                                   env_deactivate):
 | 
			
		||||
    filename = str(tmpdir.join('spack.yaml'))
 | 
			
		||||
    default_viewdir = str(tmpdir.join('default-view'))
 | 
			
		||||
    combin_viewdir = str(tmpdir.join('combinatorial-view'))
 | 
			
		||||
    with open(filename, 'w') as f:
 | 
			
		||||
        f.write("""\
 | 
			
		||||
env:
 | 
			
		||||
  definitions:
 | 
			
		||||
    - packages: [mpileaks, cmake]
 | 
			
		||||
    - compilers: ['%%gcc', '%%clang']
 | 
			
		||||
  specs:
 | 
			
		||||
    - matrix:
 | 
			
		||||
        - [$packages]
 | 
			
		||||
        - [$compilers]
 | 
			
		||||
 | 
			
		||||
  view:
 | 
			
		||||
    default:
 | 
			
		||||
      root: %s
 | 
			
		||||
      select: ['%%gcc']
 | 
			
		||||
    combinatorial:
 | 
			
		||||
      root: %s
 | 
			
		||||
      exclude: [callpath %%gcc]
 | 
			
		||||
      projections:
 | 
			
		||||
        'all': '${package}/${version}-${compilername}'""" % (default_viewdir,
 | 
			
		||||
                                                             combin_viewdir))
 | 
			
		||||
    with tmpdir.as_cwd():
 | 
			
		||||
        env('create', 'test', './spack.yaml')
 | 
			
		||||
        with ev.read('test'):
 | 
			
		||||
            install()
 | 
			
		||||
 | 
			
		||||
        shell = env('activate', '--sh', 'test')
 | 
			
		||||
        assert 'PATH' in shell
 | 
			
		||||
        assert os.path.join(default_viewdir, 'bin') in shell
 | 
			
		||||
 | 
			
		||||
        test = ev.read('test')
 | 
			
		||||
        for _, spec in test.concretized_specs():
 | 
			
		||||
            if not spec.satisfies('callpath%gcc'):
 | 
			
		||||
                assert os.path.exists(
 | 
			
		||||
                    os.path.join(combin_viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                                 (spec.version, spec.compiler.name)))
 | 
			
		||||
            else:
 | 
			
		||||
                assert not os.path.exists(
 | 
			
		||||
                    os.path.join(combin_viewdir, spec.name, '%s-%s' %
 | 
			
		||||
                                 (spec.version, spec.compiler.name)))
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
from spack.spec import ConflictsInSpecError, SpecError
 | 
			
		||||
from spack.version import ver
 | 
			
		||||
from spack.test.conftest import MockPackage, MockPackageMultiRepo
 | 
			
		||||
import spack.compilers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_spec(abstract, concrete):
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,18 @@ def no_chdir():
 | 
			
		||||
        assert os.getcwd() == original_wd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture(scope='function', autouse=True)
 | 
			
		||||
def reset_compiler_cache():
 | 
			
		||||
    """Ensure that the compiler cache is not shared across Spack tests
 | 
			
		||||
 | 
			
		||||
    This cache can cause later tests to fail if left in a state incompatible
 | 
			
		||||
    with the new configuration. Since tests can make almost unlimited changes
 | 
			
		||||
    to their setup, default to not use the compiler cache across tests."""
 | 
			
		||||
    spack.compilers._compiler_cache = {}
 | 
			
		||||
    yield
 | 
			
		||||
    spack.compilers._compiler_cache = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture(scope='session', autouse=True)
 | 
			
		||||
def mock_stage(tmpdir_factory):
 | 
			
		||||
    """Mocks up a fake stage directory for use by tests."""
 | 
			
		||||
 
 | 
			
		||||
@@ -28,4 +28,4 @@ class Mpich(Package):
 | 
			
		||||
    provides('mpi@:1', when='@:1')
 | 
			
		||||
 | 
			
		||||
    def install(self, spec, prefix):
 | 
			
		||||
        pass
 | 
			
		||||
        touch(prefix.mpich)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ class Mpileaks(Package):
 | 
			
		||||
    libs = None
 | 
			
		||||
 | 
			
		||||
    def install(self, spec, prefix):
 | 
			
		||||
        pass
 | 
			
		||||
        touch(prefix.mpileaks)
 | 
			
		||||
 | 
			
		||||
    def setup_environment(self, senv, renv):
 | 
			
		||||
        renv.set('FOOBAR', self.name)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user