# 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 os
import sys
import pytest
import llnl.util.filesystem as fs
import spack.environment as ev
import spack.spec
from spack.main import SpackCommand
dev_build = SpackCommand('dev-build')
install = SpackCommand('install')
env = SpackCommand('env')
pytestmark = pytest.mark.skipif(sys.platform == "win32",
reason="does not run on windows")
[docs]def test_dev_build_basics(tmpdir, mock_packages, install_mockery):
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' % tmpdir)
spec.concretize()
assert 'dev_path' in spec.variants
with tmpdir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
dev_build('dev-build-test-install@0.0.0')
assert spec.package.filename in os.listdir(spec.prefix)
with open(os.path.join(spec.prefix, spec.package.filename), 'r') as f:
assert f.read() == spec.package.replacement_string
assert os.path.exists(str(tmpdir))
[docs]def test_dev_build_before(tmpdir, mock_packages, install_mockery):
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' % tmpdir)
spec.concretize()
with tmpdir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
dev_build('-b', 'edit', 'dev-build-test-install@0.0.0')
assert spec.package.filename in os.listdir(os.getcwd())
with open(spec.package.filename, 'r') as f:
assert f.read() == spec.package.original_string
assert not os.path.exists(spec.prefix)
[docs]def test_dev_build_until(tmpdir, mock_packages, install_mockery):
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' % tmpdir)
spec.concretize()
with tmpdir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
dev_build('-u', 'edit', 'dev-build-test-install@0.0.0')
assert spec.package.filename in os.listdir(os.getcwd())
with open(spec.package.filename, 'r') as f:
assert f.read() == spec.package.replacement_string
assert not os.path.exists(spec.prefix)
assert not spack.store.db.query(spec, installed=True)
[docs]def test_dev_build_until_last_phase(tmpdir, mock_packages, install_mockery):
# Test that we ignore the last_phase argument if it is already last
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' % tmpdir)
spec.concretize()
with tmpdir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
dev_build('-u', 'install', 'dev-build-test-install@0.0.0')
assert spec.package.filename in os.listdir(os.getcwd())
with open(spec.package.filename, 'r') as f:
assert f.read() == spec.package.replacement_string
assert os.path.exists(spec.prefix)
assert spack.store.db.query(spec, installed=True)
assert os.path.exists(str(tmpdir))
[docs]def test_dev_build_before_until(tmpdir, mock_packages, install_mockery, capsys):
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' % tmpdir)
spec.concretize()
with tmpdir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
with pytest.raises(SystemExit):
dev_build('-u', 'edit', '-b', 'edit',
'dev-build-test-install@0.0.0')
bad_phase = 'phase_that_does_not_exist'
not_allowed = 'is not a valid phase'
not_installed = 'was not installed'
out = dev_build('-u', bad_phase, 'dev-build-test-install@0.0.0',
fail_on_error=False)
assert bad_phase in out
assert not_allowed in out
assert not_installed in out
out = dev_build('-b', bad_phase, 'dev-build-test-install@0.0.0',
fail_on_error=False)
assert bad_phase in out
assert not_allowed in out
assert not_installed in out
[docs]def print_spack_cc(*args):
# Eat arguments and print environment variable to test
print(os.environ.get('CC', ''))
# `module unload cray-libsci` in test environment causes failure
# It does not fail for actual installs
# build_environment.py imports module directly, so we monkeypatch it there
# rather than in module_cmd
[docs]def mock_module_noop(*args):
pass
[docs]def test_dev_build_drop_in(tmpdir, mock_packages, monkeypatch,
install_mockery, working_env):
monkeypatch.setattr(os, 'execvp', print_spack_cc)
monkeypatch.setattr(spack.build_environment, 'module', mock_module_noop)
with tmpdir.as_cwd():
output = dev_build('-b', 'edit', '--drop-in', 'sh',
'dev-build-test-install@0.0.0')
assert "lib/spack/env" in output
[docs]def test_dev_build_fails_already_installed(tmpdir, mock_packages,
install_mockery):
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' % tmpdir)
spec.concretize()
with tmpdir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
dev_build('dev-build-test-install@0.0.0')
output = dev_build('dev-build-test-install@0.0.0', fail_on_error=False)
assert 'Already installed in %s' % spec.prefix in output
[docs]def test_dev_build_fails_no_spec():
output = dev_build(fail_on_error=False)
assert 'requires a package spec argument' in output
[docs]def test_dev_build_fails_multiple_specs(mock_packages):
output = dev_build('libelf', 'libdwarf', fail_on_error=False)
assert 'only takes one spec' in output
[docs]def test_dev_build_fails_nonexistent_package_name(mock_packages):
output = dev_build('no_such_package', fail_on_error=False)
assert "No package for 'no_such_package' was found" in output
[docs]def test_dev_build_fails_no_version(mock_packages):
output = dev_build('dev-build-test-install', fail_on_error=False)
assert 'dev-build spec must have a single, concrete version' in output
[docs]def test_dev_build_env(tmpdir, mock_packages, install_mockery,
mutable_mock_env_path):
"""Test Spack does dev builds for packages in develop section of env."""
# setup dev-build-test-install package for dev build
build_dir = tmpdir.mkdir('build')
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' %
build_dir)
spec.concretize()
with build_dir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
# setup environment
envdir = tmpdir.mkdir('env')
with envdir.as_cwd():
with open('spack.yaml', 'w') as f:
f.write("""\
env:
specs:
- dev-build-test-install@0.0.0
develop:
dev-build-test-install:
spec: dev-build-test-install@0.0.0
path: %s
""" % os.path.relpath(str(build_dir), start=str(envdir)))
env('create', 'test', './spack.yaml')
with ev.read('test'):
install()
assert spec.package.filename in os.listdir(spec.prefix)
with open(os.path.join(spec.prefix, spec.package.filename), 'r') as f:
assert f.read() == spec.package.replacement_string
[docs]def test_dev_build_env_version_mismatch(tmpdir, mock_packages, install_mockery,
mutable_mock_env_path):
"""Test Spack constraints concretization by develop specs."""
# setup dev-build-test-install package for dev build
build_dir = tmpdir.mkdir('build')
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' % tmpdir)
spec.concretize()
with build_dir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
# setup environment
envdir = tmpdir.mkdir('env')
with envdir.as_cwd():
with open('spack.yaml', 'w') as f:
f.write("""\
env:
specs:
- dev-build-test-install@0.0.0
develop:
dev-build-test-install:
spec: dev-build-test-install@1.1.1
path: %s
""" % build_dir)
env('create', 'test', './spack.yaml')
with ev.read('test'):
with pytest.raises(RuntimeError):
install()
[docs]def test_dev_build_multiple(tmpdir, mock_packages, install_mockery,
mutable_mock_env_path, mock_fetch):
"""Test spack install with multiple developer builds"""
# setup dev-build-test-install package for dev build
# Wait to concretize inside the environment to set dev_path on the specs;
# without the environment, the user would need to set dev_path for both the
# root and dependency if they wanted a dev build for both.
leaf_dir = tmpdir.mkdir('leaf')
leaf_spec = spack.spec.Spec('dev-build-test-install@0.0.0')
with leaf_dir.as_cwd():
with open(leaf_spec.package.filename, 'w') as f:
f.write(leaf_spec.package.original_string)
# setup dev-build-test-dependent package for dev build
# don't concretize outside environment -- dev info will be wrong
root_dir = tmpdir.mkdir('root')
root_spec = spack.spec.Spec('dev-build-test-dependent@0.0.0')
with root_dir.as_cwd():
with open(root_spec.package.filename, 'w') as f:
f.write(root_spec.package.original_string)
# setup environment
envdir = tmpdir.mkdir('env')
with envdir.as_cwd():
with open('spack.yaml', 'w') as f:
f.write("""\
env:
specs:
- dev-build-test-install@0.0.0
- dev-build-test-dependent@0.0.0
develop:
dev-build-test-install:
path: %s
spec: dev-build-test-install@0.0.0
dev-build-test-dependent:
spec: dev-build-test-dependent@0.0.0
path: %s
""" % (leaf_dir, root_dir))
env('create', 'test', './spack.yaml')
with ev.read('test'):
# Do concretization inside environment for dev info
leaf_spec.concretize()
root_spec.concretize()
# Do install
install()
for spec in (leaf_spec, root_spec):
assert spec.package.filename in os.listdir(spec.prefix)
with open(os.path.join(spec.prefix, spec.package.filename), 'r') as f:
assert f.read() == spec.package.replacement_string
[docs]def test_dev_build_env_dependency(tmpdir, mock_packages, install_mockery,
mock_fetch, mutable_mock_env_path):
"""
Test non-root specs in an environment are properly marked for dev builds.
"""
# setup dev-build-test-install package for dev build
build_dir = tmpdir.mkdir('build')
spec = spack.spec.Spec('dependent-of-dev-build@0.0.0')
dep_spec = spack.spec.Spec('dev-build-test-install')
with build_dir.as_cwd():
with open(dep_spec.package.filename, 'w') as f:
f.write(dep_spec.package.original_string)
# setup environment
envdir = tmpdir.mkdir('env')
with envdir.as_cwd():
with open('spack.yaml', 'w') as f:
f.write("""\
env:
specs:
- dependent-of-dev-build@0.0.0
develop:
dev-build-test-install:
spec: dev-build-test-install@0.0.0
path: %s
""" % os.path.relpath(str(build_dir), start=str(envdir)))
env('create', 'test', './spack.yaml')
with ev.read('test'):
# concretize in the environment to get the dev build info
# equivalent to setting dev_build and dev_path variants
# on all specs above
spec.concretize()
dep_spec.concretize()
install()
# Ensure that both specs installed properly
assert dep_spec.package.filename in os.listdir(dep_spec.prefix)
assert os.path.exists(spec.prefix)
# Ensure variants set properly; ensure build_dir is absolute and normalized
for dep in (dep_spec, spec['dev-build-test-install']):
assert dep.satisfies('dev_path=%s' % build_dir)
assert spec.satisfies('^dev_path=*')
[docs]@pytest.mark.parametrize('test_spec', ['dev-build-test-install',
'dependent-of-dev-build'])
def test_dev_build_rebuild_on_source_changes(
test_spec, tmpdir, mock_packages, install_mockery,
mutable_mock_env_path, mock_fetch):
"""Test dev builds rebuild on changes to source code.
``test_spec = dev-build-test-install`` tests rebuild for changes to package
``test_spec = dependent-of-dev-build`` tests rebuild for changes to dep
"""
# setup dev-build-test-install package for dev build
build_dir = tmpdir.mkdir('build')
spec = spack.spec.Spec('dev-build-test-install@0.0.0 dev_path=%s' %
build_dir)
spec.concretize()
def reset_string():
with build_dir.as_cwd():
with open(spec.package.filename, 'w') as f:
f.write(spec.package.original_string)
reset_string()
# setup environment
envdir = tmpdir.mkdir('env')
with envdir.as_cwd():
with open('spack.yaml', 'w') as f:
f.write("""\
env:
specs:
- %s@0.0.0
develop:
dev-build-test-install:
spec: dev-build-test-install@0.0.0
path: %s
""" % (test_spec, build_dir))
env('create', 'test', './spack.yaml')
with ev.read('test'):
install()
reset_string() # so the package will accept rebuilds
fs.touch(os.path.join(str(build_dir), 'test'))
output = install()
assert 'Installing %s' % test_spec in output