Source code for spack.test.build_systems

# Copyright 2013-2022 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)

import glob
import os
import sys

import pytest

import llnl.util.filesystem as fs

import spack.environment
import spack.platforms
import spack.repo
from spack.build_environment import ChildError, get_std_cmake_args, setup_package
from spack.spec import Spec
from spack.util.executable import which

DATA_PATH = os.path.join(spack.paths.test_path, 'data')

pytestmark = pytest.mark.skipif(sys.platform == "win32",
                                reason="does not run on windows")


[docs]@pytest.mark.parametrize( 'directory', glob.iglob(os.path.join(DATA_PATH, 'make', 'affirmative', '*')) ) def test_affirmative_make_check(directory, config, mock_packages, working_env): """Tests that Spack correctly detects targets in a Makefile.""" # Get a fake package s = Spec('mpich') s.concretize() pkg = spack.repo.get(s) setup_package(pkg, False) with fs.working_dir(directory): assert pkg._has_make_target('check') pkg._if_make_target_execute('check')
[docs]@pytest.mark.parametrize( 'directory', glob.iglob(os.path.join(DATA_PATH, 'make', 'negative', '*')) ) @pytest.mark.regression('9067') def test_negative_make_check(directory, config, mock_packages, working_env): """Tests that Spack correctly ignores false positives in a Makefile.""" # Get a fake package s = Spec('mpich') s.concretize() pkg = spack.repo.get(s) setup_package(pkg, False) with fs.working_dir(directory): assert not pkg._has_make_target('check') pkg._if_make_target_execute('check')
[docs]@pytest.mark.skipif(not which('ninja'), reason='ninja is not installed') @pytest.mark.parametrize( 'directory', glob.iglob(os.path.join(DATA_PATH, 'ninja', 'affirmative', '*')) ) def test_affirmative_ninja_check( directory, config, mock_packages, working_env): """Tests that Spack correctly detects targets in a Ninja build script.""" # Get a fake package s = Spec('mpich') s.concretize() pkg = spack.repo.get(s) setup_package(pkg, False) with fs.working_dir(directory): assert pkg._has_ninja_target('check') pkg._if_ninja_target_execute('check') # Clean up Ninja files for filename in glob.iglob('.ninja_*'): os.remove(filename)
[docs]@pytest.mark.skipif(not which('ninja'), reason='ninja is not installed') @pytest.mark.parametrize( 'directory', glob.iglob(os.path.join(DATA_PATH, 'ninja', 'negative', '*')) ) def test_negative_ninja_check(directory, config, mock_packages, working_env): """Tests that Spack correctly ignores false positives in a Ninja build script.""" # Get a fake package s = Spec('mpich') s.concretize() pkg = spack.repo.get(s) setup_package(pkg, False) with fs.working_dir(directory): assert not pkg._has_ninja_target('check') pkg._if_ninja_target_execute('check')
[docs]def test_cmake_std_args(config, mock_packages): # Call the function on a CMakePackage instance s = Spec('cmake-client') s.concretize() pkg = spack.repo.get(s) assert pkg.std_cmake_args == get_std_cmake_args(pkg) # Call it on another kind of package s = Spec('mpich') s.concretize() pkg = spack.repo.get(s) assert get_std_cmake_args(pkg)
[docs]def test_cmake_bad_generator(config, mock_packages): s = Spec('cmake-client') s.concretize() pkg = spack.repo.get(s) pkg.generator = 'Yellow Sticky Notes' with pytest.raises(spack.package.InstallError): get_std_cmake_args(pkg)
[docs]def test_cmake_secondary_generator(config, mock_packages): s = Spec('cmake-client') s.concretize() pkg = spack.repo.get(s) pkg.generator = 'CodeBlocks - Unix Makefiles' assert get_std_cmake_args(pkg)
[docs]@pytest.mark.usefixtures('config', 'mock_packages') class TestAutotoolsPackage(object):
[docs] def test_with_or_without(self): s = Spec('a') s.concretize() pkg = spack.repo.get(s) options = pkg.with_or_without('foo') # Ensure that values that are not representing a feature # are not used by with_or_without assert '--without-none' not in options assert '--with-bar' in options assert '--without-baz' in options assert '--no-fee' in options def activate(value): return 'something' options = pkg.with_or_without('foo', activation_value=activate) assert '--without-none' not in options assert '--with-bar=something' in options assert '--without-baz' in options assert '--no-fee' in options options = pkg.enable_or_disable('foo') assert '--disable-none' not in options assert '--enable-bar' in options assert '--disable-baz' in options assert '--disable-fee' in options options = pkg.with_or_without('bvv') assert '--with-bvv' in options options = pkg.with_or_without('lorem-ipsum', variant='lorem_ipsum') assert '--without-lorem-ipsum' in options
[docs] def test_none_is_allowed(self): s = Spec('a foo=none') s.concretize() pkg = spack.repo.get(s) options = pkg.with_or_without('foo') # Ensure that values that are not representing a feature # are not used by with_or_without assert '--with-none' not in options assert '--without-bar' in options assert '--without-baz' in options assert '--no-fee' in options
[docs] def test_libtool_archive_files_are_deleted_by_default( self, mutable_database ): # Install a package that creates a mock libtool archive s = Spec('libtool-deletion') s.concretize() s.package.do_install(explicit=True) # Assert the libtool archive is not there and we have # a log of removed files assert not os.path.exists(s.package.libtool_archive_file) search_directory = os.path.join(s.prefix, '.spack') libtool_deletion_log = fs.find( search_directory, 'removed_la_files.txt', recursive=True ) assert libtool_deletion_log
[docs] def test_libtool_archive_files_might_be_installed_on_demand( self, mutable_database, monkeypatch ): # Install a package that creates a mock libtool archive, # patch its package to preserve the installation s = Spec('libtool-deletion') s.concretize() monkeypatch.setattr(s.package, 'install_libtool_archives', True) s.package.do_install(explicit=True) # Assert libtool archives are installed assert os.path.exists(s.package.libtool_archive_file)
[docs] def test_autotools_gnuconfig_replacement(self, mutable_database): """ Tests whether only broken config.sub and config.guess are replaced with files from working alternatives from the gnuconfig package. """ s = Spec('autotools-config-replacement +patch_config_files +gnuconfig') s.concretize() s.package.do_install() with open(os.path.join(s.prefix.broken, 'config.sub')) as f: assert "gnuconfig version of config.sub" in f.read() with open(os.path.join(s.prefix.broken, 'config.guess')) as f: assert "gnuconfig version of config.guess" in f.read() with open(os.path.join(s.prefix.working, 'config.sub')) as f: assert "gnuconfig version of config.sub" not in f.read() with open(os.path.join(s.prefix.working, 'config.guess')) as f: assert "gnuconfig version of config.guess" not in f.read()
[docs] def test_autotools_gnuconfig_replacement_disabled(self, mutable_database): """ Tests whether disabling patch_config_files """ s = Spec('autotools-config-replacement ~patch_config_files +gnuconfig') s.concretize() s.package.do_install() with open(os.path.join(s.prefix.broken, 'config.sub')) as f: assert "gnuconfig version of config.sub" not in f.read() with open(os.path.join(s.prefix.broken, 'config.guess')) as f: assert "gnuconfig version of config.guess" not in f.read() with open(os.path.join(s.prefix.working, 'config.sub')) as f: assert "gnuconfig version of config.sub" not in f.read() with open(os.path.join(s.prefix.working, 'config.guess')) as f: assert "gnuconfig version of config.guess" not in f.read()
[docs] @pytest.mark.disable_clean_stage_check def test_autotools_gnuconfig_replacement_no_gnuconfig(self, mutable_database): """ Tests whether a useful error message is shown when patch_config_files is enabled, but gnuconfig is not listed as a direct build dependency. """ s = Spec('autotools-config-replacement +patch_config_files ~gnuconfig') s.concretize() msg = "Cannot patch config files: missing dependencies: gnuconfig" with pytest.raises(ChildError, match=msg): s.package.do_install()
[docs] @pytest.mark.disable_clean_stage_check def test_broken_external_gnuconfig(self, mutable_database, tmpdir): """ Tests whether we get a useful error message when gnuconfig is marked external, but the install prefix is misconfigured and no config.guess and config.sub substitute files are found in the provided prefix. """ env_dir = str(tmpdir.ensure('env', dir=True)) gnuconfig_dir = str(tmpdir.ensure('gnuconfig', dir=True)) # empty dir with open(os.path.join(env_dir, 'spack.yaml'), 'w') as f: f.write("""\ spack: specs: - 'autotools-config-replacement +patch_config_files +gnuconfig' packages: gnuconfig: buildable: false externals: - spec: gnuconfig@1.0.0 prefix: {0} """.format(gnuconfig_dir)) msg = ("Spack could not find `config.guess`.*misconfigured as an " "external package") with spack.environment.Environment(env_dir) as e: e.concretize() with pytest.raises(ChildError, match=msg): e.install_all()
[docs]@pytest.mark.usefixtures('config', 'mock_packages') class TestCMakePackage(object):
[docs] def test_define(self): s = Spec('cmake-client') s.concretize() pkg = spack.repo.get(s) for cls in (list, tuple): arg = pkg.define('MULTI', cls(['right', 'up'])) assert arg == '-DMULTI:STRING=right;up' arg = pkg.define('MULTI', fs.FileList(['/foo', '/bar'])) assert arg == '-DMULTI:STRING=/foo;/bar' arg = pkg.define('ENABLE_TRUTH', False) assert arg == '-DENABLE_TRUTH:BOOL=OFF' arg = pkg.define('ENABLE_TRUTH', True) assert arg == '-DENABLE_TRUTH:BOOL=ON' arg = pkg.define('SINGLE', 'red') assert arg == '-DSINGLE:STRING=red'
[docs] def test_define_from_variant(self): s = Spec('cmake-client multi=up,right ~truthy single=red') s.concretize() pkg = spack.repo.get(s) arg = pkg.define_from_variant('MULTI') assert arg == '-DMULTI:STRING=right;up' arg = pkg.define_from_variant('ENABLE_TRUTH', 'truthy') assert arg == '-DENABLE_TRUTH:BOOL=OFF' arg = pkg.define_from_variant('SINGLE') assert arg == '-DSINGLE:STRING=red' with pytest.raises(KeyError, match="not a variant"): pkg.define_from_variant('NONEXISTENT')
[docs]@pytest.mark.usefixtures('config', 'mock_packages') class TestGNUMirrorPackage(object):
[docs] def test_define(self): s = Spec('mirror-gnu') s.concretize() pkg = spack.repo.get(s) s = Spec('mirror-gnu-broken') s.concretize() pkg_broken = spack.repo.get(s) cls_name = type(pkg_broken).__name__ with pytest.raises(AttributeError, match=r'{0} must define a `gnu_mirror_path` ' r'attribute \[none defined\]' .format(cls_name)): pkg_broken.urls assert pkg.urls[0] == 'https://ftpmirror.gnu.org/' \ 'make/make-4.2.1.tar.gz'
[docs]@pytest.mark.usefixtures('config', 'mock_packages') class TestSourceforgePackage(object):
[docs] def test_define(self): s = Spec('mirror-sourceforge') s.concretize() pkg = spack.repo.get(s) s = Spec('mirror-sourceforge-broken') s.concretize() pkg_broken = spack.repo.get(s) cls_name = type(pkg_broken).__name__ with pytest.raises(AttributeError, match=r'{0} must define a `sourceforge_mirror_path`' r' attribute \[none defined\]' .format(cls_name)): pkg_broken.urls assert pkg.urls[0] == 'https://prdownloads.sourceforge.net/' \ 'tcl/tcl8.6.5-src.tar.gz'
[docs]@pytest.mark.usefixtures('config', 'mock_packages') class TestSourcewarePackage(object):
[docs] def test_define(self): s = Spec('mirror-sourceware') s.concretize() pkg = spack.repo.get(s) s = Spec('mirror-sourceware-broken') s.concretize() pkg_broken = spack.repo.get(s) cls_name = type(pkg_broken).__name__ with pytest.raises(AttributeError, match=r'{0} must define a `sourceware_mirror_path` ' r'attribute \[none defined\]' .format(cls_name)): pkg_broken.urls assert pkg.urls[0] == 'https://sourceware.org/pub/' \ 'bzip2/bzip2-1.0.8.tar.gz'
[docs]@pytest.mark.usefixtures('config', 'mock_packages') class TestXorgPackage(object):
[docs] def test_define(self): s = Spec('mirror-xorg') s.concretize() pkg = spack.repo.get(s) s = Spec('mirror-xorg-broken') s.concretize() pkg_broken = spack.repo.get(s) cls_name = type(pkg_broken).__name__ with pytest.raises(AttributeError, match=r'{0} must define a `xorg_mirror_path` ' r'attribute \[none defined\]' .format(cls_name)): pkg_broken.urls assert pkg.urls[0] == 'https://www.x.org/archive/individual/' \ 'util/util-macros-1.19.1.tar.bz2'
[docs]def test_cmake_define_from_variant_conditional(config, mock_packages): """Test that define_from_variant returns empty string when a condition on a variant is not met. When this is the case, the variant is not set in the spec.""" s = Spec('cmake-conditional-variants-test').concretized() assert 'example' not in s.variants assert s.package.define_from_variant('EXAMPLE', 'example') == ''
[docs]def test_autotools_args_from_conditional_variant(config, mock_packages): """Test that _activate_or_not returns an empty string when a condition on a variant is not met. When this is the case, the variant is not set in the spec.""" s = Spec('autotools-conditional-variants-test').concretized() assert 'example' not in s.variants assert len(s.package._activate_or_not('example', 'enable', 'disable')) == 0