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"
2017-04-01 16:09:34 +08:00
# include "vcpkg_Util.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 ;
2017-01-31 06:17:26 +08:00
using Dependencies : : request_type ;
2017-01-27 06:25:36 +08:00
2016-09-19 11:50:08 +08:00
static const std : : string OPTION_PURGE = " --purge " ;
2017-01-31 14:04:04 +08:00
static const std : : string OPTION_RECURSE = " --recurse " ;
2017-03-30 08:45:53 +08:00
static const std : : string OPTION_DRY_RUN = " --dry-run " ;
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-31 08:46:39 +08:00
static void remove_package ( const vcpkg_paths & paths , const package_spec & spec , StatusParagraphs * status_db )
2016-12-01 17:49:24 +08:00
{
2017-01-31 08:46:39 +08:00
StatusParagraph & pkg = * * status_db - > find ( spec . name ( ) , spec . target_triplet ( ) ) ;
2016-12-01 17:49:24 +08:00
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 ) ;
}
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 )
{
2017-01-31 05:57:43 +08:00
if ( i . plan . plan_type = = remove_plan_type : : NOT_INSTALLED )
2017-01-28 12:22:33 +08:00
{
not_installed . push_back ( & i ) ;
continue ;
}
2017-01-31 05:57:43 +08:00
if ( i . plan . plan_type = = remove_plan_type : : REMOVE )
2017-01-28 12:22:33 +08:00
{
remove . push_back ( & i ) ;
continue ;
}
2017-03-14 07:59:21 +08:00
Checks : : unreachable ( VCPKG_LINE_INFO ) ;
2017-01-28 12:22:33 +08:00
}
if ( ! not_installed . empty ( ) )
{
sort_packages_by_name ( & not_installed ) ;
System : : println ( " The following packages are not installed, so not removed: \n %s " ,
2017-03-09 14:33:28 +08:00
Strings : : join ( " \n " , not_installed , [ ] ( const package_spec_with_remove_plan * p )
{
return " " + p - > spec . toString ( ) ;
} ) ) ;
2017-01-28 12:22:33 +08:00
}
if ( ! remove . empty ( ) )
{
sort_packages_by_name ( & remove ) ;
System : : println ( " The following packages will be removed: \n %s " ,
2017-03-09 14:33:28 +08:00
Strings : : join ( " \n " , remove , [ ] ( const package_spec_with_remove_plan * p )
2017-03-30 08:45:53 +08:00
{
if ( p - > plan . request_type = = Dependencies : : request_type : : AUTO_SELECTED )
{
return " * " + p - > spec . toString ( ) ;
}
if ( p - > plan . request_type = = Dependencies : : request_type : : USER_REQUESTED )
{
return " " + p - > spec . toString ( ) ;
}
Checks : : unreachable ( VCPKG_LINE_INFO ) ;
} ) ) ;
2017-01-28 12:22:33 +08:00
}
}
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 ) ;
2017-04-01 16:09:34 +08:00
auto specs = Util : : fmap ( args . command_arguments , [ & ] ( auto & & arg )
{
auto spec = Input : : check_and_get_package_spec ( arg , default_target_triplet , example ) ;
Input : : check_triplet ( spec . target_triplet ( ) , paths ) ;
return spec ;
} ) ;
2017-03-30 08:45:53 +08:00
const std : : unordered_set < std : : string > options = args . check_and_get_optional_command_arguments ( { OPTION_PURGE , OPTION_RECURSE , OPTION_DRY_RUN } ) ;
2017-01-28 12:22:33 +08:00
const bool alsoRemoveFolderFromPackages = options . find ( OPTION_PURGE ) ! = options . end ( ) ;
2017-01-31 14:04:04 +08:00
const bool isRecursive = options . find ( OPTION_RECURSE ) ! = options . end ( ) ;
2017-03-30 08:45:53 +08:00
const bool dryRun = options . find ( OPTION_DRY_RUN ) ! = options . end ( ) ;
2016-09-19 11:50:08 +08:00
2017-02-17 16:01:14 +08:00
auto status_db = database_load_check ( paths ) ;
2017-02-16 10:38:40 +08:00
const std : : vector < package_spec_with_remove_plan > remove_plan = Dependencies : : create_remove_plan ( specs , status_db ) ;
2017-03-14 08:38:04 +08:00
Checks : : check_exit ( VCPKG_LINE_INFO , ! remove_plan . empty ( ) , " Remove plan cannot be empty " ) ;
2017-01-27 09:53:45 +08:00
2017-01-28 12:22:33 +08:00
print_plan ( remove_plan ) ;
2017-03-30 08:45:53 +08:00
if ( dryRun )
{
Checks : : exit_success ( VCPKG_LINE_INFO ) ;
}
2017-01-28 12:22:33 +08:00
const bool has_non_user_requested_packages = std : : find_if ( remove_plan . cbegin ( ) , remove_plan . cend ( ) , [ ] ( const package_spec_with_remove_plan & package ) - > bool
{
2017-01-31 06:17:26 +08:00
return package . plan . request_type ! = request_type : : USER_REQUESTED ;
2017-01-28 12:22:33 +08:00
} ) ! = remove_plan . cend ( ) ;
if ( has_non_user_requested_packages & & ! isRecursive )
{
2017-01-31 05:37:00 +08:00
System : : println ( System : : color : : warning ,
" Additional packages (*) need to be removed to complete this operation. \n "
2017-01-31 14:04:04 +08:00
" If you are sure you want to remove them, run the command with the --recurse option " ) ;
2017-03-23 08:45:39 +08:00
Checks : : exit_fail ( VCPKG_LINE_INFO ) ;
2017-01-28 12:22:33 +08:00
}
2017-01-31 04:34:36 +08:00
for ( const package_spec_with_remove_plan & action : remove_plan )
2016-09-19 11:50:08 +08:00
{
2017-02-04 10:25:43 +08:00
const std : : string display_name = action . spec . display_name ( ) ;
switch ( action . plan . plan_type )
2016-09-19 11:50:08 +08:00
{
2017-02-04 10:25:43 +08:00
case remove_plan_type : : NOT_INSTALLED :
System : : println ( System : : color : : success , " Package %s is not installed " , display_name ) ;
break ;
case remove_plan_type : : REMOVE :
System : : println ( " Removing package %s... " , display_name ) ;
remove_package ( paths , action . spec , & status_db ) ;
System : : println ( System : : color : : success , " Removing package %s... done " , display_name ) ;
break ;
case remove_plan_type : : UNKNOWN :
default :
2017-03-14 07:59:21 +08:00
Checks : : unreachable ( VCPKG_LINE_INFO ) ;
2016-09-19 11:50:08 +08:00
}
2017-01-31 04:34:36 +08:00
2017-02-04 10:25:43 +08:00
if ( alsoRemoveFolderFromPackages )
{
System : : println ( " Purging package %s... " , display_name ) ;
delete_directory ( paths . packages / action . spec . dir ( ) ) ;
System : : println ( System : : color : : success , " Purging package %s... done " , display_name ) ;
2017-01-31 04:34:36 +08:00
}
2016-09-19 11:50:08 +08:00
}
2017-01-31 04:34:36 +08:00
2017-03-23 08:45:39 +08:00
Checks : : exit_success ( VCPKG_LINE_INFO ) ;
2016-09-19 11:50:08 +08:00
}
}