2017-01-28 04:49:09 +08:00
# include "pch.h"
2016-11-07 12:12:54 +08:00
# include "vcpkg_Commands.h"
# include "vcpkg_System.h"
# include "vcpkg_Maps.h"
2016-11-08 08:20:32 +08:00
# include "SourceParagraph.h"
2017-02-28 07:52:57 +08:00
# include "Paragraphs.h"
2017-04-01 18:30:52 +08:00
# include "SortedVector.h"
2016-11-07 12:12:54 +08:00
2017-01-13 14:03:57 +08:00
namespace vcpkg : : Commands : : PortsDiff
2016-11-07 12:12:54 +08:00
{
2017-04-04 07:38:30 +08:00
struct UpdatedPort
2017-03-30 09:14:48 +08:00
{
2017-04-04 07:38:30 +08:00
static bool compare_by_name ( const UpdatedPort & left , const UpdatedPort & right )
2017-03-30 09:14:48 +08:00
{
return left . port < right . port ;
}
std : : string port ;
2017-04-04 07:18:21 +08:00
VersionDiff version_diff ;
2017-03-30 09:14:48 +08:00
} ;
2017-03-30 08:33:08 +08:00
template < class T >
2017-04-04 07:39:34 +08:00
struct SetElementPresence
2016-11-07 12:12:54 +08:00
{
2017-04-04 07:39:34 +08:00
static SetElementPresence create ( std : : vector < T > left , std : : vector < T > right )
2016-11-07 12:12:54 +08:00
{
2017-03-30 08:33:08 +08:00
// TODO: This can be done with one pass instead of three passes
2017-04-04 07:39:34 +08:00
SetElementPresence output ;
2017-03-30 08:33:08 +08:00
std : : set_difference ( left . cbegin ( ) , left . cend ( ) , right . cbegin ( ) , right . cend ( ) , std : : back_inserter ( output . only_left ) ) ;
std : : set_intersection ( left . cbegin ( ) , left . cend ( ) , right . cbegin ( ) , right . cend ( ) , std : : back_inserter ( output . both ) ) ;
std : : set_difference ( right . cbegin ( ) , right . cend ( ) , left . cbegin ( ) , left . cend ( ) , std : : back_inserter ( output . only_right ) ) ;
return output ;
2016-11-07 12:12:54 +08:00
}
2017-03-30 08:33:08 +08:00
std : : vector < T > only_left ;
std : : vector < T > both ;
std : : vector < T > only_right ;
} ;
2017-04-04 07:38:30 +08:00
static std : : vector < UpdatedPort > find_updated_ports ( const std : : vector < std : : string > & ports ,
2017-04-04 07:13:46 +08:00
const std : : map < std : : string , VersionT > & previous_names_and_versions ,
const std : : map < std : : string , VersionT > & current_names_and_versions )
2016-11-07 12:12:54 +08:00
{
2017-04-04 07:38:30 +08:00
std : : vector < UpdatedPort > output ;
2017-03-30 08:33:08 +08:00
for ( const std : : string & name : ports )
2016-11-07 12:12:54 +08:00
{
2017-04-04 07:13:46 +08:00
const VersionT & previous_version = previous_names_and_versions . at ( name ) ;
const VersionT & current_version = current_names_and_versions . at ( name ) ;
2017-03-30 08:33:08 +08:00
if ( previous_version = = current_version )
{
continue ;
}
2017-04-04 07:18:21 +08:00
output . push_back ( { name , VersionDiff ( previous_version , current_version ) } ) ;
2017-03-30 08:33:08 +08:00
}
return output ;
}
2017-04-04 07:13:46 +08:00
static void do_print_name_and_version ( const std : : vector < std : : string > & ports_to_print , const std : : map < std : : string , VersionT > & names_and_versions )
2017-03-30 08:33:08 +08:00
{
for ( const std : : string & name : ports_to_print )
{
2017-04-04 07:13:46 +08:00
const VersionT & version = names_and_versions . at ( name ) ;
2017-03-30 08:33:08 +08:00
System : : println ( " %-20s %-16s " , name , version ) ;
2016-11-07 12:12:54 +08:00
}
}
2017-04-04 07:29:11 +08:00
static std : : map < std : : string , VersionT > read_ports_from_commit ( const VcpkgPaths & paths , const std : : wstring & git_commit_id )
2016-11-07 12:12:54 +08:00
{
2017-03-11 08:34:24 +08:00
const fs : : path & git_exe = paths . get_git_exe ( ) ;
2016-11-07 12:12:54 +08:00
const fs : : path dot_git_dir = paths . root / " .git " ;
const std : : wstring ports_dir_name_as_string = paths . ports . filename ( ) . native ( ) ;
const fs : : path temp_checkout_path = paths . root / Strings : : wformat ( L " %s-%s " , ports_dir_name_as_string , git_commit_id ) ;
fs : : create_directory ( temp_checkout_path ) ;
const std : : wstring checkout_this_dir = Strings : : wformat ( LR " (. \ %s) " , ports_dir_name_as_string ) ; // Must be relative to the root of the repository
2017-03-11 08:34:24 +08:00
const std : : wstring cmd = Strings : : wformat ( LR " ( " % s " --git-dir= " % s " --work-tree= " % s " checkout %s -f -q -- %s %s & " % s " reset >NUL) " ,
git_exe . native ( ) ,
2016-11-07 12:12:54 +08:00
dot_git_dir . native ( ) ,
temp_checkout_path . native ( ) ,
git_commit_id ,
checkout_this_dir ,
2017-03-11 08:34:24 +08:00
L " .vcpkg-root " ,
git_exe . native ( ) ) ;
2017-03-11 09:03:47 +08:00
System : : cmd_execute_clean ( cmd ) ;
2017-04-09 07:26:26 +08:00
const std : : vector < SourceParagraph > source_paragraphs = Paragraphs : : load_all_ports ( paths . get_filesystem ( ) , temp_checkout_path / ports_dir_name_as_string ) ;
2017-04-04 07:13:46 +08:00
const std : : map < std : : string , VersionT > names_and_versions = Paragraphs : : extract_port_names_and_versions ( source_paragraphs ) ;
2016-11-07 12:12:54 +08:00
fs : : remove_all ( temp_checkout_path ) ;
return names_and_versions ;
}
2017-03-11 08:34:24 +08:00
static void check_commit_exists ( const fs : : path & git_exe , const std : : wstring & git_commit_id )
2016-11-29 10:07:42 +08:00
{
static const std : : string VALID_COMMIT_OUTPUT = " commit \n " ;
2017-03-14 07:17:47 +08:00
const std : : wstring cmd = Strings : : wformat ( LR " ( " % s " cat-file -t %s) " , git_exe . native ( ) , git_commit_id ) ;
2017-04-04 07:30:11 +08:00
const System : : ExitCodeAndOutput output = System : : cmd_execute_and_capture_output ( cmd ) ;
2017-03-14 08:38:04 +08:00
Checks : : check_exit ( VCPKG_LINE_INFO , output . output = = VALID_COMMIT_OUTPUT , " Invalid commit id %s " , Strings : : utf16_to_utf8 ( git_commit_id ) ) ;
2016-11-29 10:07:42 +08:00
}
2017-04-04 07:29:11 +08:00
void perform_and_exit ( const VcpkgCmdArguments & args , const VcpkgPaths & paths )
2016-11-07 12:12:54 +08:00
{
2017-01-13 14:03:57 +08:00
static const std : : string example = Strings : : format ( " The argument should be a branch/tag/hash to checkout. \n %s " , Commands : : Help : : create_example_string ( " portsdiff mybranchname " ) ) ;
2016-12-13 07:03:36 +08:00
args . check_min_arg_count ( 1 , example ) ;
args . check_max_arg_count ( 2 , example ) ;
2017-02-18 07:46:35 +08:00
args . check_and_get_optional_command_arguments ( { } ) ;
2016-11-29 10:07:42 +08:00
2017-03-11 08:34:24 +08:00
const fs : : path & git_exe = paths . get_git_exe ( ) ;
2016-11-07 12:12:54 +08:00
const std : : wstring git_commit_id_for_previous_snapshot = Strings : : utf8_to_utf16 ( args . command_arguments . at ( 0 ) ) ;
const std : : wstring git_commit_id_for_current_snapshot = args . command_arguments . size ( ) < 2 ? L " HEAD " : Strings : : utf8_to_utf16 ( args . command_arguments . at ( 1 ) ) ;
2017-03-11 08:34:24 +08:00
check_commit_exists ( git_exe , git_commit_id_for_current_snapshot ) ;
check_commit_exists ( git_exe , git_commit_id_for_previous_snapshot ) ;
2016-11-29 10:07:42 +08:00
2017-04-04 07:13:46 +08:00
const std : : map < std : : string , VersionT > current_names_and_versions = read_ports_from_commit ( paths , git_commit_id_for_current_snapshot ) ;
const std : : map < std : : string , VersionT > previous_names_and_versions = read_ports_from_commit ( paths , git_commit_id_for_previous_snapshot ) ;
2016-11-07 12:12:54 +08:00
// Already sorted, so set_difference can work on std::vector too
std : : vector < std : : string > current_ports = Maps : : extract_keys ( current_names_and_versions ) ;
std : : vector < std : : string > previous_ports = Maps : : extract_keys ( previous_names_and_versions ) ;
2017-04-04 07:39:34 +08:00
const SetElementPresence < std : : string > setp = SetElementPresence < std : : string > : : create ( current_ports , previous_ports ) ;
2016-11-07 12:12:54 +08:00
2017-03-30 08:33:08 +08:00
const std : : vector < std : : string > & added_ports = setp . only_left ;
2016-11-07 12:12:54 +08:00
if ( ! added_ports . empty ( ) )
{
System : : println ( " \n The following %d ports were added: \n " , added_ports . size ( ) ) ;
do_print_name_and_version ( added_ports , current_names_and_versions ) ;
}
2017-03-30 08:33:08 +08:00
const std : : vector < std : : string > & removed_ports = setp . only_right ;
2016-11-07 12:12:54 +08:00
if ( ! removed_ports . empty ( ) )
{
System : : println ( " \n The following %d ports were removed: \n " , removed_ports . size ( ) ) ;
do_print_name_and_version ( removed_ports , previous_names_and_versions ) ;
}
2017-03-30 08:33:08 +08:00
const std : : vector < std : : string > & common_ports = setp . both ;
2017-04-04 07:38:30 +08:00
const std : : vector < UpdatedPort > updated_ports = find_updated_ports ( common_ports , previous_names_and_versions , current_names_and_versions ) ;
2016-11-07 12:12:54 +08:00
if ( ! updated_ports . empty ( ) )
{
System : : println ( " \n The following %d ports were updated: \n " , updated_ports . size ( ) ) ;
2017-04-04 07:38:30 +08:00
for ( const UpdatedPort & p : updated_ports )
2017-03-30 08:33:08 +08:00
{
2017-04-04 07:20:12 +08:00
System : : println ( " %-20s %-16s " , p . port , p . version_diff . to_string ( ) ) ;
2017-03-30 08:33:08 +08:00
}
2016-11-07 12:12:54 +08:00
}
if ( added_ports . empty ( ) & & removed_ports . empty ( ) & & updated_ports . empty ( ) )
{
System : : println ( " There were no changes in the ports between the two commits. " ) ;
}
2017-03-23 08:45:39 +08:00
Checks : : exit_success ( VCPKG_LINE_INFO ) ;
2016-11-07 12:12:54 +08:00
}
}