# Copyright 2013-2023 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 filecmp import os import sys import pytest import spack.rewiring import spack.store from spack.spec import Spec from spack.test.relocate import text_in_bin args = ["file"] if sys.platform == "darwin": args.extend(["/usr/bin/clang++", "install_name_tool"]) else: args.extend(["g++", "patchelf"]) @pytest.mark.requires_executables(*args) @pytest.mark.parametrize("transitive", [True, False]) def test_rewire_db(mock_fetch, install_mockery, transitive): """Tests basic rewiring without binary executables.""" spec = Spec("splice-t^splice-h~foo").concretized() dep = Spec("splice-h+foo").concretized() spec.package.do_install() dep.package.do_install() spliced_spec = spec.splice(dep, transitive=transitive) assert spec.dag_hash() != spliced_spec.dag_hash() spack.rewiring.rewire(spliced_spec) # check that the prefix exists assert os.path.exists(spliced_spec.prefix) # test that it made it into the database rec = spack.store.STORE.db.get_record(spliced_spec) installed_in_db = rec.installed if rec else False assert installed_in_db # check the file in the prefix has the correct paths for node in spliced_spec.traverse(root=True): text_file_path = os.path.join(node.prefix, node.name) with open(text_file_path, "r") as f: text = f.read() for modded_spec in node.traverse(root=True): assert modded_spec.prefix in text @pytest.mark.requires_executables(*args) @pytest.mark.parametrize("transitive", [True, False]) def test_rewire_bin(mock_fetch, install_mockery, transitive): """Tests basic rewiring with binary executables.""" spec = Spec("quux").concretized() dep = Spec("garply cflags=-g").concretized() spec.package.do_install() dep.package.do_install() spliced_spec = spec.splice(dep, transitive=transitive) assert spec.dag_hash() != spliced_spec.dag_hash() spack.rewiring.rewire(spliced_spec) # check that the prefix exists assert os.path.exists(spliced_spec.prefix) # test that it made it into the database rec = spack.store.STORE.db.get_record(spliced_spec) installed_in_db = rec.installed if rec else False assert installed_in_db # check the file in the prefix has the correct paths bin_names = {"garply": "garplinator", "corge": "corgegator", "quux": "quuxifier"} for node in spliced_spec.traverse(root=True): for dep in node.traverse(root=True): bin_file_path = os.path.join(dep.prefix.bin, bin_names[dep.name]) assert text_in_bin(dep.prefix, bin_file_path) @pytest.mark.requires_executables(*args) def test_rewire_writes_new_metadata(mock_fetch, install_mockery): """Tests that new metadata was written during a rewire. Accuracy of metadata is left to other tests.""" spec = Spec("quux").concretized() dep = Spec("garply cflags=-g").concretized() spec.package.do_install() dep.package.do_install() spliced_spec = spec.splice(dep, transitive=True) spack.rewiring.rewire(spliced_spec) # test install manifests for node in spliced_spec.traverse(root=True): spack.store.STORE.layout.ensure_installed(node) manifest_file_path = os.path.join( node.prefix, spack.store.STORE.layout.metadata_dir, spack.store.STORE.layout.manifest_file_name, ) assert os.path.exists(manifest_file_path) orig_node = spec[node.name] orig_manifest_file_path = os.path.join( orig_node.prefix, spack.store.STORE.layout.metadata_dir, spack.store.STORE.layout.manifest_file_name, ) assert os.path.exists(orig_manifest_file_path) assert not filecmp.cmp(orig_manifest_file_path, manifest_file_path, shallow=False) specfile_path = os.path.join( node.prefix, spack.store.STORE.layout.metadata_dir, spack.store.STORE.layout.spec_file_name, ) assert os.path.exists(specfile_path) orig_specfile_path = os.path.join( orig_node.prefix, spack.store.STORE.layout.metadata_dir, spack.store.STORE.layout.spec_file_name, ) assert os.path.exists(orig_specfile_path) assert not filecmp.cmp(orig_specfile_path, specfile_path, shallow=False) @pytest.mark.requires_executables(*args) @pytest.mark.parametrize("transitive", [True, False]) def test_uninstall_rewired_spec(mock_fetch, install_mockery, transitive): """Test that rewired packages can be uninstalled as normal.""" spec = Spec("quux").concretized() dep = Spec("garply cflags=-g").concretized() spec.package.do_install() dep.package.do_install() spliced_spec = spec.splice(dep, transitive=transitive) spack.rewiring.rewire(spliced_spec) spliced_spec.package.do_uninstall() assert len(spack.store.STORE.db.query(spliced_spec)) == 0 assert not os.path.exists(spliced_spec.prefix) @pytest.mark.requires_executables(*args) def test_rewire_not_installed_fails(mock_fetch, install_mockery): """Tests error when an attempt is made to rewire a package that was not previously installed.""" spec = Spec("quux").concretized() dep = Spec("garply cflags=-g").concretized() spliced_spec = spec.splice(dep, False) with pytest.raises( spack.rewiring.PackageNotInstalledError, match="failed due to missing install of build spec", ): spack.rewiring.rewire(spliced_spec)