Source code for spack.test.test_activations

# 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)

"""This includes tests for customized activation logic for specific packages
   (e.g. python and perl).
"""

import os

import pytest

from llnl.util.link_tree import MergeConflictError

import spack.package
import spack.spec
from spack.directory_layout import DirectoryLayout
from spack.filesystem_view import YamlFilesystemView
from spack.repo import RepoPath


[docs]def create_ext_pkg(name, prefix, extendee_spec, monkeypatch): ext_spec = spack.spec.Spec(name) ext_spec._concrete = True ext_spec.package.spec.prefix = prefix ext_pkg = ext_spec.package # temporarily override extendee_spec property on the package monkeypatch.setattr(ext_pkg.__class__, "extendee_spec", extendee_spec) return ext_pkg
[docs]def create_python_ext_pkg(name, prefix, python_spec, monkeypatch, namespace=None): ext_pkg = create_ext_pkg(name, prefix, python_spec, monkeypatch) ext_pkg.py_namespace = namespace return ext_pkg
[docs]def create_dir_structure(tmpdir, dir_structure): for fname, children in dir_structure.items(): tmpdir.ensure(fname, dir=fname.endswith('/')) if children: create_dir_structure(tmpdir.join(fname), children)
[docs]@pytest.fixture() def builtin_and_mock_packages(): # These tests use mock_repo packages to test functionality of builtin # packages for python and perl. To test this we put the mock repo at lower # precedence than the builtin repo, so we test builtin.perl against # builtin.mock.perl-extension. repo_dirs = [spack.paths.packages_path, spack.paths.mock_packages_path] path = RepoPath(*repo_dirs) with spack.repo.use_repositories(path): yield
[docs]@pytest.fixture() def python_and_extension_dirs(tmpdir, builtin_and_mock_packages): python_dirs = { 'bin/': { 'python': None }, 'lib/': { 'python2.7/': { 'site-packages/': None } } } python_name = 'python' python_prefix = tmpdir.join(python_name) create_dir_structure(python_prefix, python_dirs) python_spec = spack.spec.Spec('python@2.7.12') python_spec._concrete = True python_spec.package.spec.prefix = str(python_prefix) ext_dirs = { 'bin/': { 'py-ext-tool': None }, 'lib/': { 'python2.7/': { 'site-packages/': { 'py-extension1/': { 'sample.py': None } } } } } ext_name = 'py-extension1' ext_prefix = tmpdir.join(ext_name) create_dir_structure(ext_prefix, ext_dirs) easy_install_location = 'lib/python2.7/site-packages/easy-install.pth' with open(str(ext_prefix.join(easy_install_location)), 'w') as f: f.write("""path/to/ext1.egg path/to/setuptools.egg""") return str(python_prefix), str(ext_prefix)
[docs]@pytest.fixture() def namespace_extensions(tmpdir, builtin_and_mock_packages): ext1_dirs = { 'bin/': { 'py-ext-tool1': None }, 'lib/': { 'python2.7/': { 'site-packages/': { 'examplenamespace/': { '__init__.py': None, 'ext1_sample.py': None } } } } } ext2_dirs = { 'bin/': { 'py-ext-tool2': None }, 'lib/': { 'python2.7/': { 'site-packages/': { 'examplenamespace/': { '__init__.py': None, 'ext2_sample.py': None } } } } } ext1_name = 'py-extension1' ext1_prefix = tmpdir.join(ext1_name) create_dir_structure(ext1_prefix, ext1_dirs) ext2_name = 'py-extension2' ext2_prefix = tmpdir.join(ext2_name) create_dir_structure(ext2_prefix, ext2_dirs) return str(ext1_prefix), str(ext2_prefix), 'examplenamespace'
[docs]def test_python_activation_with_files(tmpdir, python_and_extension_dirs, monkeypatch, builtin_and_mock_packages): python_prefix, ext_prefix = python_and_extension_dirs python_spec = spack.spec.Spec('python@2.7.12') python_spec._concrete = True python_spec.package.spec.prefix = python_prefix ext_pkg = create_python_ext_pkg( 'py-extension1', ext_prefix, python_spec, monkeypatch) python_pkg = python_spec.package python_pkg.activate(ext_pkg, python_pkg.view()) assert os.path.exists(os.path.join(python_prefix, 'bin/py-ext-tool')) easy_install_location = 'lib/python2.7/site-packages/easy-install.pth' with open(os.path.join(python_prefix, easy_install_location), 'r') as f: easy_install_contents = f.read() assert 'ext1.egg' in easy_install_contents assert 'setuptools.egg' not in easy_install_contents
[docs]def test_python_activation_view(tmpdir, python_and_extension_dirs, builtin_and_mock_packages, monkeypatch): python_prefix, ext_prefix = python_and_extension_dirs python_spec = spack.spec.Spec('python@2.7.12') python_spec._concrete = True python_spec.package.spec.prefix = python_prefix ext_pkg = create_python_ext_pkg('py-extension1', ext_prefix, python_spec, monkeypatch) view_dir = str(tmpdir.join('view')) layout = DirectoryLayout(view_dir) view = YamlFilesystemView(view_dir, layout) python_pkg = python_spec.package python_pkg.activate(ext_pkg, view) assert not os.path.exists(os.path.join(python_prefix, 'bin/py-ext-tool')) assert os.path.exists(os.path.join(view_dir, 'bin/py-ext-tool'))
[docs]def test_python_ignore_namespace_init_conflict( tmpdir, namespace_extensions, builtin_and_mock_packages, monkeypatch): """Test the view update logic in PythonPackage ignores conflicting instances of __init__ for packages which are in the same namespace. """ ext1_prefix, ext2_prefix, py_namespace = namespace_extensions python_spec = spack.spec.Spec('python@2.7.12') python_spec._concrete = True ext1_pkg = create_python_ext_pkg('py-extension1', ext1_prefix, python_spec, monkeypatch, py_namespace) ext2_pkg = create_python_ext_pkg('py-extension2', ext2_prefix, python_spec, monkeypatch, py_namespace) view_dir = str(tmpdir.join('view')) layout = DirectoryLayout(view_dir) view = YamlFilesystemView(view_dir, layout) python_pkg = python_spec.package python_pkg.activate(ext1_pkg, view) # Normally handled by Package.do_activate, but here we activate directly view.extensions_layout.add_extension(python_spec, ext1_pkg.spec) python_pkg.activate(ext2_pkg, view) f1 = 'lib/python2.7/site-packages/examplenamespace/ext1_sample.py' f2 = 'lib/python2.7/site-packages/examplenamespace/ext2_sample.py' init_file = 'lib/python2.7/site-packages/examplenamespace/__init__.py' assert os.path.exists(os.path.join(view_dir, f1)) assert os.path.exists(os.path.join(view_dir, f2)) assert os.path.exists(os.path.join(view_dir, init_file))
[docs]def test_python_keep_namespace_init( tmpdir, namespace_extensions, builtin_and_mock_packages, monkeypatch): """Test the view update logic in PythonPackage keeps the namespace __init__ file as long as one package in the namespace still exists. """ ext1_prefix, ext2_prefix, py_namespace = namespace_extensions python_spec = spack.spec.Spec('python@2.7.12') python_spec._concrete = True ext1_pkg = create_python_ext_pkg('py-extension1', ext1_prefix, python_spec, monkeypatch, py_namespace) ext2_pkg = create_python_ext_pkg('py-extension2', ext2_prefix, python_spec, monkeypatch, py_namespace) view_dir = str(tmpdir.join('view')) layout = DirectoryLayout(view_dir) view = YamlFilesystemView(view_dir, layout) python_pkg = python_spec.package python_pkg.activate(ext1_pkg, view) # Normally handled by Package.do_activate, but here we activate directly view.extensions_layout.add_extension(python_spec, ext1_pkg.spec) python_pkg.activate(ext2_pkg, view) view.extensions_layout.add_extension(python_spec, ext2_pkg.spec) f1 = 'lib/python2.7/site-packages/examplenamespace/ext1_sample.py' init_file = 'lib/python2.7/site-packages/examplenamespace/__init__.py' python_pkg.deactivate(ext1_pkg, view) view.extensions_layout.remove_extension(python_spec, ext1_pkg.spec) assert not os.path.exists(os.path.join(view_dir, f1)) assert os.path.exists(os.path.join(view_dir, init_file)) python_pkg.deactivate(ext2_pkg, view) view.extensions_layout.remove_extension(python_spec, ext2_pkg.spec) assert not os.path.exists(os.path.join(view_dir, init_file))
[docs]def test_python_namespace_conflict(tmpdir, namespace_extensions, monkeypatch, builtin_and_mock_packages): """Test the view update logic in PythonPackage reports an error when two python extensions with different namespaces have a conflicting __init__ file. """ ext1_prefix, ext2_prefix, py_namespace = namespace_extensions other_namespace = py_namespace + 'other' python_spec = spack.spec.Spec('python@2.7.12') python_spec._concrete = True ext1_pkg = create_python_ext_pkg('py-extension1', ext1_prefix, python_spec, monkeypatch, py_namespace) ext2_pkg = create_python_ext_pkg('py-extension2', ext2_prefix, python_spec, monkeypatch, other_namespace) view_dir = str(tmpdir.join('view')) layout = DirectoryLayout(view_dir) view = YamlFilesystemView(view_dir, layout) python_pkg = python_spec.package python_pkg.activate(ext1_pkg, view) view.extensions_layout.add_extension(python_spec, ext1_pkg.spec) with pytest.raises(MergeConflictError): python_pkg.activate(ext2_pkg, view)
[docs]@pytest.fixture() def perl_and_extension_dirs(tmpdir, builtin_and_mock_packages): perl_dirs = { 'bin/': { 'perl': None }, 'lib/': { 'site_perl/': { '5.24.1/': { 'x86_64-linux/': None } } } } perl_name = 'perl' perl_prefix = tmpdir.join(perl_name) create_dir_structure(perl_prefix, perl_dirs) perl_spec = spack.spec.Spec('perl@5.24.1') perl_spec._concrete = True perl_spec.package.spec.prefix = str(perl_prefix) ext_dirs = { 'bin/': { 'perl-ext-tool': None }, 'lib/': { 'site_perl/': { '5.24.1/': { 'x86_64-linux/': { 'TestExt/': { } } } } } } ext_name = 'perl-extension' ext_prefix = tmpdir.join(ext_name) create_dir_structure(ext_prefix, ext_dirs) return str(perl_prefix), str(ext_prefix)
[docs]def test_perl_activation(tmpdir, builtin_and_mock_packages, monkeypatch): # Note the lib directory is based partly on the perl version perl_spec = spack.spec.Spec('perl@5.24.1') perl_spec._concrete = True perl_name = 'perl' tmpdir.ensure(perl_name, dir=True) perl_prefix = str(tmpdir.join(perl_name)) # Set the prefix on the package's spec reference because that is a copy of # the original spec perl_spec.package.spec.prefix = perl_prefix ext_name = 'perl-extension' tmpdir.ensure(ext_name, dir=True) ext_pkg = create_ext_pkg( ext_name, str(tmpdir.join(ext_name)), perl_spec, monkeypatch) perl_pkg = perl_spec.package perl_pkg.activate(ext_pkg, perl_pkg.view())
[docs]def test_perl_activation_with_files(tmpdir, perl_and_extension_dirs, monkeypatch, builtin_and_mock_packages): perl_prefix, ext_prefix = perl_and_extension_dirs perl_spec = spack.spec.Spec('perl@5.24.1') perl_spec._concrete = True perl_spec.package.spec.prefix = perl_prefix ext_pkg = create_ext_pkg( 'perl-extension', ext_prefix, perl_spec, monkeypatch) perl_pkg = perl_spec.package perl_pkg.activate(ext_pkg, perl_pkg.view()) assert os.path.exists(os.path.join(perl_prefix, 'bin/perl-ext-tool'))
[docs]def test_perl_activation_view(tmpdir, perl_and_extension_dirs, monkeypatch, builtin_and_mock_packages): perl_prefix, ext_prefix = perl_and_extension_dirs perl_spec = spack.spec.Spec('perl@5.24.1') perl_spec._concrete = True perl_spec.package.spec.prefix = perl_prefix ext_pkg = create_ext_pkg( 'perl-extension', ext_prefix, perl_spec, monkeypatch) view_dir = str(tmpdir.join('view')) layout = DirectoryLayout(view_dir) view = YamlFilesystemView(view_dir, layout) perl_pkg = perl_spec.package perl_pkg.activate(ext_pkg, view) assert not os.path.exists(os.path.join(perl_prefix, 'bin/perl-ext-tool')) assert os.path.exists(os.path.join(view_dir, 'bin/perl-ext-tool'))
[docs]def test_is_activated_upstream_extendee(tmpdir, builtin_and_mock_packages, monkeypatch): """When an extendee is installed upstream, make sure that the extension spec is never considered to be globally activated for it. """ extendee_spec = spack.spec.Spec('python') extendee_spec._concrete = True python_name = 'python' tmpdir.ensure(python_name, dir=True) python_prefix = str(tmpdir.join(python_name)) # Set the prefix on the package's spec reference because that is a copy of # the original spec extendee_spec.package.spec.prefix = python_prefix monkeypatch.setattr(extendee_spec.package.__class__, 'installed_upstream', True) ext_name = 'py-extension1' tmpdir.ensure(ext_name, dir=True) ext_pkg = create_ext_pkg( ext_name, str(tmpdir.join(ext_name)), extendee_spec, monkeypatch) # The view should not be checked at all if the extendee is installed # upstream, so use 'None' here mock_view = None assert not ext_pkg.is_activated(mock_view)