2017-01-28 04:49:09 +08:00
# include "pch.h"
2016-09-19 11:50:08 +08:00
# include "vcpkg_Commands.h"
2017-01-26 11:32:50 +08:00
# include "vcpkglib.h"
2016-09-19 11:50:08 +08:00
# include "vcpkg_System.h"
2016-10-01 07:49:30 +08:00
# include "vcpkg_Input.h"
2017-01-27 06:25:36 +08:00
# include "vcpkg_Dependencies.h"
2016-09-19 11:50:08 +08:00
2017-01-13 14:03:57 +08:00
namespace vcpkg : : Commands : : Remove
2016-09-19 11:50:08 +08:00
{
2017-01-27 09:53:45 +08:00
using Dependencies : : package_spec_with_remove_plan ;
2017-01-27 06:25:36 +08:00
using Dependencies : : remove_plan_type ;
2016-09-19 11:50:08 +08:00
static const std : : string OPTION_PURGE = " --purge " ;
2017-01-28 12:22:33 +08:00
static const std : : string OPTION_RECURSIVE = " --recursive " ;
2016-09-19 11:50:08 +08:00
static void delete_directory ( const fs : : path & directory )
{
std : : error_code ec ;
fs : : remove_all ( directory , ec ) ;
if ( ! ec )
{
System : : println ( System : : color : : success , " Cleaned up %s " , directory . string ( ) ) ;
}
if ( fs : : exists ( directory ) )
{
System : : println ( System : : color : : warning , " Some files in %s were unable to be removed. Close any editors operating in this directory and retry. " , directory . string ( ) ) ;
}
}
2017-01-27 06:25:36 +08:00
static remove_plan_type deinstall_package_plan (
2016-12-01 17:49:24 +08:00
const StatusParagraphs : : iterator package_it ,
const StatusParagraphs & status_db ,
std : : vector < const StatusParagraph * > & dependencies_out )
{
dependencies_out . clear ( ) ;
if ( package_it = = status_db . end ( ) | | ( * package_it ) - > state = = install_state_t : : not_installed )
{
2017-01-27 06:25:36 +08:00
return remove_plan_type : : NOT_INSTALLED ;
2016-12-01 17:49:24 +08:00
}
auto & pkg = ( * package_it ) - > package ;
for ( auto & & inst_pkg : status_db )
{
if ( inst_pkg - > want ! = want_t : : install )
continue ;
if ( inst_pkg - > package . spec . target_triplet ( ) ! = pkg . spec . target_triplet ( ) )
continue ;
const auto & deps = inst_pkg - > package . depends ;
if ( std : : find ( deps . begin ( ) , deps . end ( ) , pkg . spec . name ( ) ) ! = deps . end ( ) )
{
dependencies_out . push_back ( inst_pkg . get ( ) ) ;
}
}
if ( ! dependencies_out . empty ( ) )
2017-01-27 06:25:36 +08:00
return remove_plan_type : : DEPENDENCIES_NOT_SATISFIED ;
2016-12-01 17:49:24 +08:00
2017-01-27 09:53:45 +08:00
return remove_plan_type : : REMOVE ;
2016-12-01 17:49:24 +08:00
}
static void deinstall_package ( const vcpkg_paths & paths , const package_spec & spec , StatusParagraphs & status_db )
{
auto package_it = status_db . find ( spec . name ( ) , spec . target_triplet ( ) ) ;
if ( package_it = = status_db . end ( ) )
{
System : : println ( System : : color : : success , " Package %s is not installed " , spec ) ;
return ;
}
auto & pkg = * * package_it ;
std : : vector < const StatusParagraph * > deps ;
auto plan = deinstall_package_plan ( package_it , status_db , deps ) ;
switch ( plan )
{
2017-01-27 06:25:36 +08:00
case remove_plan_type : : NOT_INSTALLED :
2016-12-01 17:49:24 +08:00
System : : println ( System : : color : : success , " Package %s is not installed " , spec ) ;
return ;
2017-01-27 06:25:36 +08:00
case remove_plan_type : : DEPENDENCIES_NOT_SATISFIED :
2016-12-01 17:49:24 +08:00
System : : println ( System : : color : : error , " Error: Cannot remove package %s: " , spec ) ;
for ( auto & & dep : deps )
{
System : : println ( " %s depends on %s " , dep - > package . displayname ( ) , pkg . package . displayname ( ) ) ;
}
exit ( EXIT_FAILURE ) ;
2017-01-27 09:53:45 +08:00
case remove_plan_type : : REMOVE :
2016-12-01 17:49:24 +08:00
break ;
default :
Checks : : unreachable ( ) ;
}
pkg . want = want_t : : purge ;
pkg . state = install_state_t : : half_installed ;
write_update ( paths , pkg ) ;
std : : fstream listfile ( paths . listfile_path ( pkg . package ) , std : : ios_base : : in | std : : ios_base : : binary ) ;
if ( listfile )
{
std : : vector < fs : : path > dirs_touched ;
std : : string suffix ;
while ( std : : getline ( listfile , suffix ) )
{
if ( ! suffix . empty ( ) & & suffix . back ( ) = = ' \r ' )
suffix . pop_back ( ) ;
std : : error_code ec ;
auto target = paths . installed / suffix ;
auto status = fs : : status ( target , ec ) ;
if ( ec )
{
System : : println ( System : : color : : error , " failed: %s " , ec . message ( ) ) ;
continue ;
}
if ( fs : : is_directory ( status ) )
{
dirs_touched . push_back ( target ) ;
}
else if ( fs : : is_regular_file ( status ) )
{
fs : : remove ( target , ec ) ;
if ( ec )
{
System : : println ( System : : color : : error , " failed: %s: %s " , target . u8string ( ) , ec . message ( ) ) ;
}
}
else if ( ! fs : : status_known ( status ) )
{
System : : println ( System : : color : : warning , " Warning: unknown status: %s " , target . u8string ( ) ) ;
}
else
{
System : : println ( System : : color : : warning , " Warning: %s: cannot handle file type " , target . u8string ( ) ) ;
}
}
auto b = dirs_touched . rbegin ( ) ;
auto e = dirs_touched . rend ( ) ;
for ( ; b ! = e ; + + b )
{
if ( fs : : directory_iterator ( * b ) = = fs : : directory_iterator ( ) )
{
std : : error_code ec ;
fs : : remove ( * b , ec ) ;
if ( ec )
{
System : : println ( System : : color : : error , " failed: %s " , ec . message ( ) ) ;
}
}
}
listfile . close ( ) ;
fs : : remove ( paths . listfile_path ( pkg . package ) ) ;
}
pkg . state = install_state_t : : not_installed ;
write_update ( paths , pkg ) ;
System : : println ( System : : color : : success , " Package %s was successfully removed " , pkg . package . displayname ( ) ) ;
}
2017-01-28 12:22:33 +08:00
static void sort_packages_by_name ( std : : vector < const package_spec_with_remove_plan * > * packages )
{
std : : sort ( packages - > begin ( ) , packages - > end ( ) , [ ] ( const package_spec_with_remove_plan * left , const package_spec_with_remove_plan * right ) - > bool
{
return left - > spec . name ( ) < right - > spec . name ( ) ;
} ) ;
}
static void print_plan ( const std : : vector < package_spec_with_remove_plan > & plan )
{
std : : vector < const package_spec_with_remove_plan * > not_installed ;
std : : vector < const package_spec_with_remove_plan * > remove ;
for ( const package_spec_with_remove_plan & i : plan )
{
if ( i . plan . type = = remove_plan_type : : NOT_INSTALLED )
{
not_installed . push_back ( & i ) ;
continue ;
}
if ( i . plan . type = = remove_plan_type : : REMOVE | | i . plan . type = = remove_plan_type : : REMOVE_USER_REQUESTED )
{
remove . push_back ( & i ) ;
continue ;
}
Checks : : unreachable ( ) ;
}
//std::copy_if(plan.cbegin(), plan.cend(), std::back_inserter(not_installed), [](const package_spec_with_remove_plan& i)
// {
// return i.plan.type == remove_plan_type::NOT_INSTALLED;
// });
if ( ! not_installed . empty ( ) )
{
sort_packages_by_name ( & not_installed ) ;
System : : println ( " The following packages are not installed, so not removed: \n %s " ,
Strings : : Joiner : : on ( " \n " ) . prefix ( " " ) . join ( not_installed , [ ] ( const package_spec_with_remove_plan * p )
{
return p - > spec . toString ( ) ;
} ) ) ;
}
if ( ! remove . empty ( ) )
{
sort_packages_by_name ( & remove ) ;
System : : println ( " The following packages will be removed: \n %s " ,
Strings : : Joiner : : on ( " \n " ) . join ( remove , [ ] ( const package_spec_with_remove_plan * p )
{
if ( p - > plan . type = = remove_plan_type : : REMOVE_USER_REQUESTED )
{
return " " + p - > spec . toString ( ) ;
}
return " * " + p - > spec . toString ( ) ;
} ) ) ;
}
}
2017-01-13 14:03:57 +08:00
void perform_and_exit ( const vcpkg_cmd_arguments & args , const vcpkg_paths & paths , const triplet & default_target_triplet )
2016-09-19 11:50:08 +08:00
{
2017-01-13 14:03:57 +08:00
static const std : : string example = Commands : : Help : : create_example_string ( " remove zlib zlib:x64-windows curl boost " ) ;
2016-12-13 07:03:36 +08:00
args . check_min_arg_count ( 1 , example ) ;
2016-10-01 02:24:04 +08:00
2016-09-19 11:50:08 +08:00
const std : : unordered_set < std : : string > options = args . check_and_get_optional_command_arguments ( { OPTION_PURGE } ) ;
auto status_db = database_load_check ( paths ) ;
2016-12-13 07:05:49 +08:00
std : : vector < package_spec > specs = Input : : check_and_get_package_specs ( args . command_arguments , default_target_triplet , example ) ;
2016-10-01 08:02:39 +08:00
Input : : check_triplets ( specs , paths ) ;
2017-01-28 12:22:33 +08:00
const bool alsoRemoveFolderFromPackages = options . find ( OPTION_PURGE ) ! = options . end ( ) ;
const bool isRecursive = options . find ( OPTION_PURGE ) ! = options . end ( ) ;
2016-09-19 11:50:08 +08:00
2017-01-27 09:53:45 +08:00
const std : : vector < package_spec_with_remove_plan > remove_plan = Dependencies : : create_remove_plan ( paths , specs , status_db ) ;
Checks : : check_exit ( ! remove_plan . empty ( ) , " Remove plan cannot be empty " ) ;
2017-01-28 12:22:33 +08:00
print_plan ( remove_plan ) ;
const bool has_non_user_requested_packages = std : : find_if ( remove_plan . cbegin ( ) , remove_plan . cend ( ) , [ ] ( const package_spec_with_remove_plan & package ) - > bool
{
return package . plan . type = = remove_plan_type : : REMOVE ;
} ) ! = remove_plan . cend ( ) ;
if ( has_non_user_requested_packages & & ! isRecursive )
{
System : : println ( System : : color : : warning , " Additional packages (*) need to be removed to complete this operation. \n "
" If you are sure you want to remove them, run the command with the --recursive option " ) ;
exit ( EXIT_FAILURE ) ;
}
//exit(EXIT_SUCCESS);
2016-09-19 11:50:08 +08:00
for ( const package_spec & spec : specs )
{
deinstall_package ( paths , spec , status_db ) ;
if ( alsoRemoveFolderFromPackages )
{
const fs : : path spec_package_dir = paths . packages / spec . dir ( ) ;
delete_directory ( spec_package_dir ) ;
}
}
exit ( EXIT_SUCCESS ) ;
}
}