Allow spack install --overwrite for nonexistent or multiple packages (#9201)
				
					
				
			* Allow overwrite nonexistent and multiple packages initial implementation give one prompt to users instead of a prompt per spec testing * flake * bugfix: install overwrite check each spec against installed * python3 compliance for filter/map
This commit is contained in:
		@@ -283,36 +283,42 @@ def install(parser, args, **kwargs):
 | 
			
		||||
    reporter.specs = specs
 | 
			
		||||
    with reporter:
 | 
			
		||||
        if args.overwrite:
 | 
			
		||||
            # If we asked to overwrite an existing spec we must ensure that:
 | 
			
		||||
            # 1. We have only one spec
 | 
			
		||||
            # 2. The spec is already installed
 | 
			
		||||
            assert len(specs) == 1, \
 | 
			
		||||
                "only one spec is allowed when overwriting an installation"
 | 
			
		||||
 | 
			
		||||
            spec = specs[0]
 | 
			
		||||
            t = spack.store.db.query(spec)
 | 
			
		||||
            assert len(t) == 1, "to overwrite a spec you must install it first"
 | 
			
		||||
 | 
			
		||||
            # Give the user a last chance to think about overwriting an already
 | 
			
		||||
            # existing installation
 | 
			
		||||
            installed = list(filter(lambda x: x,
 | 
			
		||||
                                    map(spack.store.db.query_one, specs)))
 | 
			
		||||
            if not args.yes_to_all:
 | 
			
		||||
                tty.msg('The following package will be reinstalled:\n')
 | 
			
		||||
 | 
			
		||||
                display_args = {
 | 
			
		||||
                    'long': True,
 | 
			
		||||
                    'show_flags': True,
 | 
			
		||||
                    'variants': True
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                spack.cmd.display_specs(t, **display_args)
 | 
			
		||||
                if installed:
 | 
			
		||||
                    tty.msg('The following package specs will be '
 | 
			
		||||
                            'reinstalled:\n')
 | 
			
		||||
                    spack.cmd.display_specs(installed, **display_args)
 | 
			
		||||
 | 
			
		||||
                not_installed = list(filter(lambda x: x not in installed,
 | 
			
		||||
                                            specs))
 | 
			
		||||
                if not_installed:
 | 
			
		||||
                    tty.msg('The following package specs are not installed and'
 | 
			
		||||
                            ' the --overwrite flag was given. The package spec'
 | 
			
		||||
                            ' will be newly installed:\n')
 | 
			
		||||
                    spack.cmd.display_specs(not_installed, **display_args)
 | 
			
		||||
 | 
			
		||||
                # We have some specs, so one of the above must have been true
 | 
			
		||||
                answer = tty.get_yes_or_no(
 | 
			
		||||
                    'Do you want to proceed?', default=False
 | 
			
		||||
                )
 | 
			
		||||
                if not answer:
 | 
			
		||||
                    tty.die('Reinstallation aborted.')
 | 
			
		||||
 | 
			
		||||
            with fs.replace_directory_transaction(specs[0].prefix):
 | 
			
		||||
                install_spec(args, kwargs, abstract_specs[0], specs[0])
 | 
			
		||||
            for abstract, concrete in zip(abstract_specs, specs):
 | 
			
		||||
                if concrete in installed:
 | 
			
		||||
                    with fs.replace_directory_transaction(concrete.prefix):
 | 
			
		||||
                        install_spec(args, kwargs, abstract, concrete)
 | 
			
		||||
                else:
 | 
			
		||||
                    install_spec(args, kwargs, abstract, concrete)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            for abstract, concrete in zip(abstract_specs, specs):
 | 
			
		||||
 
 | 
			
		||||
@@ -185,14 +185,6 @@ def test_show_log_on_error(mock_packages, mock_archive, mock_fetch,
 | 
			
		||||
def test_install_overwrite(
 | 
			
		||||
        mock_packages, mock_archive, mock_fetch, config, install_mockery
 | 
			
		||||
):
 | 
			
		||||
    # It's not possible to overwrite something that is not yet installed
 | 
			
		||||
    with pytest.raises(AssertionError):
 | 
			
		||||
        install('--overwrite', 'libdwarf')
 | 
			
		||||
 | 
			
		||||
    # --overwrite requires a single spec
 | 
			
		||||
    with pytest.raises(AssertionError):
 | 
			
		||||
        install('--overwrite', 'libdwarf', 'libelf')
 | 
			
		||||
 | 
			
		||||
    # Try to install a spec and then to reinstall it.
 | 
			
		||||
    spec = Spec('libdwarf')
 | 
			
		||||
    spec.concretize()
 | 
			
		||||
@@ -217,6 +209,61 @@ def test_install_overwrite(
 | 
			
		||||
    assert fs.hash_directory(spec.prefix) != bad_md5
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_install_overwrite_not_installed(
 | 
			
		||||
        mock_packages, mock_archive, mock_fetch, config, install_mockery
 | 
			
		||||
):
 | 
			
		||||
    # Try to install a spec and then to reinstall it.
 | 
			
		||||
    spec = Spec('libdwarf')
 | 
			
		||||
    spec.concretize()
 | 
			
		||||
 | 
			
		||||
    assert not os.path.exists(spec.prefix)
 | 
			
		||||
 | 
			
		||||
    install('--overwrite', '-y', 'libdwarf')
 | 
			
		||||
    assert os.path.exists(spec.prefix)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_install_overwrite_multiple(
 | 
			
		||||
        mock_packages, mock_archive, mock_fetch, config, install_mockery
 | 
			
		||||
):
 | 
			
		||||
    # Try to install a spec and then to reinstall it.
 | 
			
		||||
    libdwarf = Spec('libdwarf')
 | 
			
		||||
    libdwarf.concretize()
 | 
			
		||||
 | 
			
		||||
    install('libdwarf')
 | 
			
		||||
 | 
			
		||||
    cmake = Spec('cmake')
 | 
			
		||||
    cmake.concretize()
 | 
			
		||||
 | 
			
		||||
    install('cmake')
 | 
			
		||||
 | 
			
		||||
    assert os.path.exists(libdwarf.prefix)
 | 
			
		||||
    expected_libdwarf_md5 = fs.hash_directory(libdwarf.prefix)
 | 
			
		||||
 | 
			
		||||
    assert os.path.exists(cmake.prefix)
 | 
			
		||||
    expected_cmake_md5 = fs.hash_directory(cmake.prefix)
 | 
			
		||||
 | 
			
		||||
    # Modify the first installation to be sure the content is not the same
 | 
			
		||||
    # as the one after we reinstalled
 | 
			
		||||
    with open(os.path.join(libdwarf.prefix, 'only_in_old'), 'w') as f:
 | 
			
		||||
        f.write('This content is here to differentiate installations.')
 | 
			
		||||
    with open(os.path.join(cmake.prefix, 'only_in_old'), 'w') as f:
 | 
			
		||||
        f.write('This content is here to differentiate installations.')
 | 
			
		||||
 | 
			
		||||
    bad_libdwarf_md5 = fs.hash_directory(libdwarf.prefix)
 | 
			
		||||
    bad_cmake_md5 = fs.hash_directory(cmake.prefix)
 | 
			
		||||
 | 
			
		||||
    assert bad_libdwarf_md5 != expected_libdwarf_md5
 | 
			
		||||
    assert bad_cmake_md5 != expected_cmake_md5
 | 
			
		||||
 | 
			
		||||
    install('--overwrite', '-y', 'libdwarf', 'cmake')
 | 
			
		||||
    assert os.path.exists(libdwarf.prefix)
 | 
			
		||||
    assert os.path.exists(cmake.prefix)
 | 
			
		||||
    assert fs.hash_directory(libdwarf.prefix) == expected_libdwarf_md5
 | 
			
		||||
    assert fs.hash_directory(cmake.prefix) == expected_cmake_md5
 | 
			
		||||
    assert fs.hash_directory(libdwarf.prefix) != bad_libdwarf_md5
 | 
			
		||||
    assert fs.hash_directory(cmake.prefix) != bad_cmake_md5
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.usefixtures(
 | 
			
		||||
    'mock_packages', 'mock_archive', 'mock_fetch', 'config', 'install_mockery',
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user