Add --format to depend-info. (#1080)
* Add --format to depend-info. Add error handling and testing to depend-info switch handling. Documentation update: https://github.com/microsoft/vcpkg-docs/pull/89 * xtree => x-tree, can not => cannot
This commit is contained in:
Родитель
2e31d678b4
Коммит
528fe75d7c
|
@ -466,16 +466,31 @@ DECLARE_MESSAGE(CmdAddVersionOptSkipFormatChk, (), "", "Skips the formatting che
|
|||
DECLARE_MESSAGE(CmdAddVersionOptSkipVersionFormatChk, (), "", "Skips the version format check.")
|
||||
DECLARE_MESSAGE(CmdAddVersionOptVerbose, (), "", "Print success messages instead of just errors.")
|
||||
DECLARE_MESSAGE(CmdContactOptSurvey, (), "", "Launch default browser to the current vcpkg survey")
|
||||
DECLARE_MESSAGE(CmdDependInfoOptDepth, (), "", "Show recursion depth in output")
|
||||
DECLARE_MESSAGE(CmdDependInfoOptDGML, (), "", "Creates graph on basis of dgml")
|
||||
DECLARE_MESSAGE(CmdDependInfoOptDot, (), "", "Creates graph on basis of dot")
|
||||
DECLARE_MESSAGE(CmdDependInfoOptMaxRecurse, (), "", "Set max recursion depth, a value of -1 indicates no limit")
|
||||
DECLARE_MESSAGE(CmdDependInfoOptSort,
|
||||
DECLARE_MESSAGE(CmdDependInfoFormatConflict,
|
||||
(),
|
||||
"",
|
||||
"Set sort order for the list of dependencies, accepted values are: lexicographical, topological "
|
||||
"(default), x-tree, "
|
||||
"reverse")
|
||||
"Conflicting formats specified. Only one of --format, --dgml, or --dot are accepted.")
|
||||
DECLARE_MESSAGE(CmdDependInfoFormatHelp,
|
||||
(),
|
||||
"The alternatives in ``s must not be localized.",
|
||||
"Choose output format, one of `list`, `tree`, `dot`, or `dgml`.")
|
||||
DECLARE_MESSAGE(
|
||||
CmdDependInfoFormatInvalid,
|
||||
(msg::value),
|
||||
"The alternatives in ``s must not be localized. {value} is what the user specified.",
|
||||
"--format={value} is not a recognized format. --format must be one of `list`, `tree`, `dot`, or `dgml`.")
|
||||
DECLARE_MESSAGE(CmdDependInfoShowDepthFormatMismatch,
|
||||
(),
|
||||
"",
|
||||
"--show-depth can only be used with `list` and `tree` formats.")
|
||||
DECLARE_MESSAGE(CmdDependInfoXtreeTree, (), "", "--sort=x-tree cannot be used with formats other than tree")
|
||||
DECLARE_MESSAGE(CmdDependInfoOptDepth, (), "", "Show recursion depth in `list` output.")
|
||||
DECLARE_MESSAGE(CmdDependInfoOptMaxRecurse, (), "", "Set max recursion depth. Default is no limit.")
|
||||
DECLARE_MESSAGE(CmdDependInfoOptSort,
|
||||
(),
|
||||
"The alternatives in ``s must not be localized, but the localized text can explain what each value "
|
||||
"means. The value `reverse` means 'reverse-topological'.",
|
||||
"Choose sort order for the `list` format, one of `lexicographical`, `topological` (default), `reverse`")
|
||||
DECLARE_MESSAGE(CmdEditOptAll, (), "", "Open editor into the port as well as the port-specific buildtree subfolder")
|
||||
DECLARE_MESSAGE(CmdEditOptBuildTrees, (), "", "Open editor into the port-specific buildtree subfolder")
|
||||
DECLARE_MESSAGE(CmdEnvOptions, (msg::path, msg::env_var), "", "Add installed {path} to {env_var}")
|
||||
|
@ -1844,7 +1859,7 @@ DECLARE_MESSAGE(MultipleFeatures,
|
|||
DECLARE_MESSAGE(MutuallyExclusiveOption,
|
||||
(msg::value, msg::option),
|
||||
"{value} is a second {option} switch",
|
||||
"--{value} can not be used with --{option}.")
|
||||
"--{value} cannot be used with --{option}.")
|
||||
DECLARE_MESSAGE(NavigateToNPS, (msg::url), "", "Please navigate to {url} in your preferred browser.")
|
||||
DECLARE_MESSAGE(NewConfigurationAlreadyExists,
|
||||
(msg::path),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <vcpkg/base/fwd/expected.h>
|
||||
|
||||
#include <vcpkg/fwd/triplet.h>
|
||||
#include <vcpkg/fwd/vcpkgcmdarguments.h>
|
||||
#include <vcpkg/fwd/vcpkgpaths.h>
|
||||
|
@ -8,6 +10,31 @@ namespace vcpkg::Commands::DependInfo
|
|||
{
|
||||
extern const CommandStructure COMMAND_STRUCTURE;
|
||||
|
||||
enum class DependInfoSortMode
|
||||
{
|
||||
Lexicographical,
|
||||
Topological,
|
||||
ReverseTopological
|
||||
};
|
||||
|
||||
enum class DependInfoFormat
|
||||
{
|
||||
List,
|
||||
Tree,
|
||||
Dot,
|
||||
Dgml
|
||||
};
|
||||
|
||||
struct DependInfoStrategy
|
||||
{
|
||||
DependInfoSortMode sort_mode;
|
||||
DependInfoFormat format;
|
||||
int max_depth;
|
||||
bool show_depth;
|
||||
};
|
||||
|
||||
ExpectedL<DependInfoStrategy> determine_depend_info_mode(const ParsedArguments& args);
|
||||
|
||||
void perform_and_exit(const VcpkgCmdArguments& args,
|
||||
const VcpkgPaths& paths,
|
||||
Triplet default_triplet,
|
||||
|
|
|
@ -272,11 +272,17 @@
|
|||
"CmdAddVersionOptSkipVersionFormatChk": "Skips the version format check.",
|
||||
"CmdAddVersionOptVerbose": "Print success messages instead of just errors.",
|
||||
"CmdContactOptSurvey": "Launch default browser to the current vcpkg survey",
|
||||
"CmdDependInfoOptDGML": "Creates graph on basis of dgml",
|
||||
"CmdDependInfoOptDepth": "Show recursion depth in output",
|
||||
"CmdDependInfoOptDot": "Creates graph on basis of dot",
|
||||
"CmdDependInfoOptMaxRecurse": "Set max recursion depth, a value of -1 indicates no limit",
|
||||
"CmdDependInfoOptSort": "Set sort order for the list of dependencies, accepted values are: lexicographical, topological (default), x-tree, reverse",
|
||||
"CmdDependInfoFormatConflict": "Conflicting formats specified. Only one of --format, --dgml, or --dot are accepted.",
|
||||
"CmdDependInfoFormatHelp": "Choose output format, one of `list`, `tree`, `dot`, or `dgml`.",
|
||||
"_CmdDependInfoFormatHelp.comment": "The alternatives in ``s must not be localized.",
|
||||
"CmdDependInfoFormatInvalid": "--format={value} is not a recognized format. --format must be one of `list`, `tree`, `dot`, or `dgml`.",
|
||||
"_CmdDependInfoFormatInvalid.comment": "The alternatives in ``s must not be localized. {value} is what the user specified.",
|
||||
"CmdDependInfoOptDepth": "Show recursion depth in `list` output.",
|
||||
"CmdDependInfoOptMaxRecurse": "Set max recursion depth. Default is no limit.",
|
||||
"CmdDependInfoOptSort": "Choose sort order for the `list` format, one of `lexicographical`, `topological` (default), `reverse`",
|
||||
"_CmdDependInfoOptSort.comment": "The alternatives in ``s must not be localized, but the localized text can explain what each value means. The value `reverse` means 'reverse-topological'.",
|
||||
"CmdDependInfoShowDepthFormatMismatch": "--show-depth can only be used with `list` and `tree` formats.",
|
||||
"CmdDependInfoXtreeTree": "--sort=x-tree cannot be used with formats other than tree",
|
||||
"CmdEditOptAll": "Open editor into the port as well as the port-specific buildtree subfolder",
|
||||
"CmdEditOptBuildTrees": "Open editor into the port-specific buildtree subfolder",
|
||||
"CmdEnvOptions": "Add installed {path} to {env_var}",
|
||||
|
@ -1034,7 +1040,7 @@
|
|||
"_MultiArch.comment": "An example of {option} is editable.",
|
||||
"MultipleFeatures": "{package_name} declares {feature} multiple times; please ensure that features have distinct names",
|
||||
"_MultipleFeatures.comment": "An example of {package_name} is zlib. An example of {feature} is avisynthplus.",
|
||||
"MutuallyExclusiveOption": "--{value} can not be used with --{option}.",
|
||||
"MutuallyExclusiveOption": "--{value} cannot be used with --{option}.",
|
||||
"_MutuallyExclusiveOption.comment": "{value} is a second {option} switch An example of {option} is editable.",
|
||||
"NavigateToNPS": "Please navigate to {url} in your preferred browser.",
|
||||
"_NavigateToNPS.comment": "An example of {url} is https://github.com/microsoft/vcpkg.",
|
||||
|
@ -1565,10 +1571,10 @@
|
|||
"_UnrecognizedCommand$.comment": "\n'${p0}' (aka 'commandline.inputs[0]') is a parameter of type 'string'\n",
|
||||
"Use$ToGetHelp": "Use ${p0} to get help",
|
||||
"_Use$ToGetHelp.comment": "\n'${p0}' is a parameter of type 'string'\n",
|
||||
"FatalTheRootFolder$CanNotBeCreated": "Fatal: The root folder '${p0}' can not be created",
|
||||
"_FatalTheRootFolder$CanNotBeCreated.comment": "\n'${p0}' (aka 'this.homeFolder.fsPath') is a parameter of type 'string'\n",
|
||||
"FatalTheGlobalConfigurationFile$CanNotBeCreated": "Fatal: The global configuration file '${p0}' can not be created",
|
||||
"_FatalTheGlobalConfigurationFile$CanNotBeCreated.comment": "\n'${p0}' (aka 'this.globalConfig.fsPath') is a parameter of type 'string'\n",
|
||||
"FatalTheRootFolder$CannotBeCreated": "Fatal: The root folder '${p0}' cannot be created",
|
||||
"_FatalTheRootFolder$CannotBeCreated.comment": "\n'${p0}' (aka 'this.homeFolder.fsPath') is a parameter of type 'string'\n",
|
||||
"FatalTheGlobalConfigurationFile$CannotBeCreated": "Fatal: The global configuration file '${p0}' cannot be created",
|
||||
"_FatalTheGlobalConfigurationFile$CannotBeCreated.comment": "\n'${p0}' (aka 'this.globalConfig.fsPath') is a parameter of type 'string'\n",
|
||||
"VCPKGCOMMANDWasNotSet": "VCPKG_COMMAND was not set",
|
||||
"RunningVcpkgInternallyReturnedANonzeroExitCode$": "Running vcpkg internally returned a nonzero exit code: ${p0}",
|
||||
"_RunningVcpkgInternallyReturnedANonzeroExitCode$.comment": "\n'${p0}' is a parameter of type 'number'\n",
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <vcpkg/commands.dependinfo.h>
|
||||
#include <vcpkg/vcpkgcmdarguments.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
using namespace vcpkg;
|
||||
using namespace vcpkg::Commands::DependInfo;
|
||||
|
||||
TEST_CASE ("determine_depend_info_mode no args", "[depend-info]")
|
||||
{
|
||||
ParsedArguments pa;
|
||||
auto result = determine_depend_info_mode(pa).value_or_exit(VCPKG_LINE_INFO);
|
||||
REQUIRE(result.sort_mode == DependInfoSortMode::Topological);
|
||||
REQUIRE(result.format == DependInfoFormat::List);
|
||||
REQUIRE(result.max_depth == INT_MAX);
|
||||
REQUIRE(!result.show_depth);
|
||||
}
|
||||
|
||||
TEST_CASE ("determine_depend_info_mode formats", "[depend-info]")
|
||||
{
|
||||
ParsedArguments pa;
|
||||
DependInfoFormat expected = DependInfoFormat::List;
|
||||
SECTION ("list")
|
||||
{
|
||||
pa.settings.emplace("format", "list");
|
||||
}
|
||||
|
||||
SECTION ("tree")
|
||||
{
|
||||
pa.settings.emplace("format", "tree");
|
||||
expected = DependInfoFormat::Tree;
|
||||
}
|
||||
|
||||
SECTION ("tree sort")
|
||||
{
|
||||
pa.settings.emplace("sort", "x-tree");
|
||||
expected = DependInfoFormat::Tree;
|
||||
}
|
||||
|
||||
SECTION ("tree tree sort")
|
||||
{
|
||||
pa.settings.emplace("format", "tree");
|
||||
pa.settings.emplace("sort", "x-tree");
|
||||
expected = DependInfoFormat::Tree;
|
||||
}
|
||||
|
||||
SECTION ("dot")
|
||||
{
|
||||
pa.switches.insert("dot");
|
||||
expected = DependInfoFormat::Dot;
|
||||
}
|
||||
|
||||
SECTION ("dot format")
|
||||
{
|
||||
pa.settings.emplace("format", "dot");
|
||||
expected = DependInfoFormat::Dot;
|
||||
}
|
||||
|
||||
SECTION ("dot and format")
|
||||
{
|
||||
pa.switches.insert("dot");
|
||||
pa.settings.emplace("format", "dot");
|
||||
expected = DependInfoFormat::Dot;
|
||||
}
|
||||
|
||||
SECTION ("dgml")
|
||||
{
|
||||
pa.switches.insert("dgml");
|
||||
expected = DependInfoFormat::Dgml;
|
||||
}
|
||||
|
||||
SECTION ("dgml format")
|
||||
{
|
||||
pa.settings.emplace("format", "dgml");
|
||||
expected = DependInfoFormat::Dgml;
|
||||
}
|
||||
|
||||
SECTION ("dgml and format")
|
||||
{
|
||||
pa.switches.insert("dgml");
|
||||
pa.settings.emplace("format", "dgml");
|
||||
expected = DependInfoFormat::Dgml;
|
||||
}
|
||||
|
||||
auto result = determine_depend_info_mode(pa).value_or_exit(VCPKG_LINE_INFO);
|
||||
REQUIRE(result.sort_mode == DependInfoSortMode::Topological);
|
||||
REQUIRE(result.format == expected);
|
||||
REQUIRE(result.max_depth == INT_MAX);
|
||||
REQUIRE(!result.show_depth);
|
||||
}
|
||||
|
||||
TEST_CASE ("determine_depend_info_mode sorts", "[depend-info]")
|
||||
{
|
||||
ParsedArguments pa;
|
||||
DependInfoSortMode expected = DependInfoSortMode::Topological;
|
||||
|
||||
SECTION ("topological default")
|
||||
{
|
||||
// intentionally empty
|
||||
}
|
||||
|
||||
SECTION ("topological")
|
||||
{
|
||||
pa.settings.emplace("sort", "topological");
|
||||
}
|
||||
|
||||
SECTION ("reverse topological")
|
||||
{
|
||||
pa.settings.emplace("sort", "reverse");
|
||||
expected = DependInfoSortMode::ReverseTopological;
|
||||
}
|
||||
|
||||
SECTION ("lexicographical")
|
||||
{
|
||||
pa.settings.emplace("sort", "lexicographical");
|
||||
expected = DependInfoSortMode::Lexicographical;
|
||||
}
|
||||
|
||||
auto result = determine_depend_info_mode(pa).value_or_exit(VCPKG_LINE_INFO);
|
||||
REQUIRE(result.sort_mode == expected);
|
||||
REQUIRE(result.format == DependInfoFormat::List);
|
||||
REQUIRE(result.max_depth == INT_MAX);
|
||||
REQUIRE(!result.show_depth);
|
||||
}
|
||||
|
||||
TEST_CASE ("determine_depend_info_mode max_depth", "[depend-info]")
|
||||
{
|
||||
ParsedArguments pa;
|
||||
int expected = INT_MAX;
|
||||
SECTION ("default")
|
||||
{
|
||||
// intentionally empty
|
||||
}
|
||||
|
||||
SECTION ("zero")
|
||||
{
|
||||
expected = 0;
|
||||
pa.settings.emplace("max-recurse", "0");
|
||||
}
|
||||
|
||||
SECTION ("negative one")
|
||||
{
|
||||
expected = INT_MAX;
|
||||
pa.settings.emplace("max-recurse", "-1");
|
||||
}
|
||||
|
||||
SECTION ("negative")
|
||||
{
|
||||
expected = INT_MAX;
|
||||
pa.settings.emplace("max-recurse", "-10");
|
||||
}
|
||||
|
||||
SECTION ("positive")
|
||||
{
|
||||
expected = 2;
|
||||
pa.settings.emplace("max-recurse", "2");
|
||||
}
|
||||
|
||||
auto result = determine_depend_info_mode(pa).value_or_exit(VCPKG_LINE_INFO);
|
||||
REQUIRE(result.sort_mode == DependInfoSortMode::Topological);
|
||||
REQUIRE(result.format == DependInfoFormat::List);
|
||||
REQUIRE(result.max_depth == expected);
|
||||
REQUIRE(!result.show_depth);
|
||||
}
|
||||
|
||||
TEST_CASE ("determine_depend_info_mode show_depth", "[depend-info]")
|
||||
{
|
||||
ParsedArguments pa;
|
||||
pa.switches.emplace("show-depth");
|
||||
auto result = determine_depend_info_mode(pa).value_or_exit(VCPKG_LINE_INFO);
|
||||
REQUIRE(result.sort_mode == DependInfoSortMode::Topological);
|
||||
REQUIRE(result.format == DependInfoFormat::List);
|
||||
REQUIRE(result.max_depth == INT_MAX);
|
||||
REQUIRE(result.show_depth);
|
||||
}
|
||||
|
||||
TEST_CASE ("determine_depend_info_mode errors", "[depend-info]")
|
||||
{
|
||||
ParsedArguments pa;
|
||||
auto expected = LocalizedString::from_raw("error: ");
|
||||
|
||||
SECTION ("bad format")
|
||||
{
|
||||
pa.settings.emplace("format", "frobinate");
|
||||
expected.append_raw(
|
||||
"--format=frobinate is not a recognized format. --format must be one of `list`, `tree`, `dot`, or `dgml`.");
|
||||
}
|
||||
|
||||
SECTION ("bad sort")
|
||||
{
|
||||
pa.settings.emplace("sort", "frobinate");
|
||||
expected.append_raw("Value of --sort must be one of 'lexicographical', 'topological', 'reverse'.");
|
||||
}
|
||||
|
||||
SECTION ("bad legacy switches")
|
||||
{
|
||||
pa.settings.emplace("format", "list");
|
||||
expected.append_raw("Conflicting formats specified. Only one of --format, --dgml, or --dot are accepted.");
|
||||
|
||||
SECTION ("dot")
|
||||
{
|
||||
pa.switches.emplace("dot");
|
||||
}
|
||||
|
||||
SECTION ("dgml")
|
||||
{
|
||||
pa.switches.emplace("dot");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION ("bad format sort tree")
|
||||
{
|
||||
pa.settings.emplace("format", "list");
|
||||
pa.settings.emplace("sort", "x-tree");
|
||||
expected.append_raw("--sort=x-tree cannot be used with formats other than tree");
|
||||
}
|
||||
|
||||
SECTION ("show depth with graphs")
|
||||
{
|
||||
pa.switches.emplace("show-depth");
|
||||
expected.append_raw("--show-depth can only be used with `list` and `tree` formats.");
|
||||
|
||||
SECTION ("dot")
|
||||
{
|
||||
pa.settings.emplace("format", "dot");
|
||||
}
|
||||
|
||||
SECTION ("dgml")
|
||||
{
|
||||
pa.settings.emplace("format", "dgml");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION ("bad max depth non numeric")
|
||||
{
|
||||
pa.settings.emplace("max-recurse", "frobinate");
|
||||
expected.append_raw("Value of --max-recurse must be an integer.");
|
||||
}
|
||||
|
||||
SECTION ("bad max depth too low")
|
||||
{
|
||||
static_assert(-2147483648 == INT_MIN, "integer range assumption");
|
||||
pa.settings.emplace("max-recurse", "-2147483649");
|
||||
expected.append_raw("Value of --max-recurse must be an integer.");
|
||||
}
|
||||
|
||||
SECTION ("bad max depth too high")
|
||||
{
|
||||
static_assert(2147483647 == INT_MAX, "integer range assumption");
|
||||
pa.settings.emplace("max-recurse", "2147483648");
|
||||
expected.append_raw("Value of --max-recurse must be an integer.");
|
||||
}
|
||||
|
||||
REQUIRE(determine_depend_info_mode(pa).error() == expected);
|
||||
}
|
|
@ -13,6 +13,8 @@
|
|||
#include <vcpkg/registries.h>
|
||||
#include <vcpkg/vcpkgcmdarguments.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace vcpkg::Commands::DependInfo
|
||||
|
@ -88,73 +90,19 @@ namespace vcpkg::Commands::DependInfo
|
|||
constexpr StringLiteral OPTION_SHOW_DEPTH = "show-depth";
|
||||
constexpr StringLiteral OPTION_MAX_RECURSE = "max-recurse";
|
||||
constexpr StringLiteral OPTION_SORT = "sort";
|
||||
constexpr StringLiteral OPTION_FORMAT = "format";
|
||||
|
||||
constexpr int NO_RECURSE_LIMIT_VALUE = -1;
|
||||
constexpr std::array<CommandSwitch, 3> DEPEND_SWITCHES = {{
|
||||
{OPTION_DOT, nullptr},
|
||||
{OPTION_DGML, nullptr},
|
||||
{OPTION_SHOW_DEPTH, []() { return msg::format(msgCmdDependInfoOptDepth); }},
|
||||
}};
|
||||
|
||||
constexpr std::array<CommandSwitch, 3> DEPEND_SWITCHES = {
|
||||
{{OPTION_DOT, []() { return msg::format(msgCmdDependInfoOptDot); }},
|
||||
{OPTION_DGML, []() { return msg::format(msgCmdDependInfoOptDGML); }},
|
||||
{OPTION_SHOW_DEPTH, []() { return msg::format(msgCmdDependInfoOptDepth); }}}};
|
||||
|
||||
constexpr std::array<CommandSetting, 2> DEPEND_SETTINGS = {
|
||||
{{OPTION_MAX_RECURSE, []() { return msg::format(msgCmdDependInfoOptMaxRecurse); }},
|
||||
{OPTION_SORT, []() { return msg::format(msgCmdDependInfoOptSort); }}}};
|
||||
|
||||
enum SortMode
|
||||
{
|
||||
Lexicographical = 0,
|
||||
Topological,
|
||||
ReverseTopological,
|
||||
Treelogical,
|
||||
Default = Topological
|
||||
};
|
||||
|
||||
int get_max_depth(const ParsedArguments& options)
|
||||
{
|
||||
auto iter = options.settings.find(OPTION_MAX_RECURSE);
|
||||
if (iter != options.settings.end())
|
||||
{
|
||||
std::string value = iter->second;
|
||||
try
|
||||
{
|
||||
return std::max(std::stoi(value), NO_RECURSE_LIMIT_VALUE);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
Checks::msg_exit_with_message(VCPKG_LINE_INFO, msgOptionMustBeInteger, msg::option = "max-depth");
|
||||
}
|
||||
}
|
||||
// No --max-depth set, default to no limit.
|
||||
return NO_RECURSE_LIMIT_VALUE;
|
||||
}
|
||||
|
||||
SortMode get_sort_mode(const ParsedArguments& options)
|
||||
{
|
||||
constexpr StringLiteral OPTION_SORT_LEXICOGRAPHICAL = "lexicographical";
|
||||
constexpr StringLiteral OPTION_SORT_TOPOLOGICAL = "topological";
|
||||
constexpr StringLiteral OPTION_SORT_REVERSE = "reverse";
|
||||
constexpr StringLiteral OPTION_SORT_TREE = "x-tree";
|
||||
|
||||
static const std::map<StringLiteral, SortMode, std::less<>> sortModesMap{
|
||||
{OPTION_SORT_LEXICOGRAPHICAL, Lexicographical},
|
||||
{OPTION_SORT_TOPOLOGICAL, Topological},
|
||||
{OPTION_SORT_REVERSE, ReverseTopological},
|
||||
{OPTION_SORT_TREE, Treelogical},
|
||||
};
|
||||
|
||||
auto iter = options.settings.find(OPTION_SORT);
|
||||
if (iter != options.settings.end())
|
||||
{
|
||||
const std::string value = Strings::ascii_to_lowercase(std::string{iter->second});
|
||||
auto it = sortModesMap.find(value);
|
||||
if (it != sortModesMap.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
Checks::msg_exit_with_message(VCPKG_LINE_INFO, msgInvalidCommandArgSort);
|
||||
}
|
||||
return Default;
|
||||
}
|
||||
constexpr std::array<CommandSetting, 3> DEPEND_SETTINGS = {{
|
||||
{OPTION_MAX_RECURSE, []() { return msg::format(msgCmdDependInfoOptMaxRecurse); }},
|
||||
{OPTION_SORT, []() { return msg::format(msgCmdDependInfoOptSort); }},
|
||||
{OPTION_FORMAT, [] { return msg::format(msgCmdDependInfoFormatHelp); }},
|
||||
}};
|
||||
|
||||
std::string create_dot_as_string(const std::vector<PackageDependInfo>& depend_info)
|
||||
{
|
||||
|
@ -209,20 +157,6 @@ namespace vcpkg::Commands::DependInfo
|
|||
return s;
|
||||
}
|
||||
|
||||
std::string create_graph_as_string(const std::set<std::string, std::less<>>& switches,
|
||||
const std::vector<PackageDependInfo>& depend_info)
|
||||
{
|
||||
if (Util::Sets::contains(switches, OPTION_DOT))
|
||||
{
|
||||
return create_dot_as_string(depend_info);
|
||||
}
|
||||
else if (Util::Sets::contains(switches, OPTION_DGML))
|
||||
{
|
||||
return create_dgml_as_string(depend_info);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void assign_depth_to_dependencies(const std::string& package,
|
||||
const int depth,
|
||||
const int max_depth,
|
||||
|
@ -239,7 +173,7 @@ namespace vcpkg::Commands::DependInfo
|
|||
if (depth > info.depth)
|
||||
{
|
||||
info.depth = depth;
|
||||
if (depth < max_depth || max_depth == NO_RECURSE_LIMIT_VALUE)
|
||||
if (depth < max_depth)
|
||||
{
|
||||
for (auto&& dependency : info.dependencies)
|
||||
{
|
||||
|
@ -278,7 +212,21 @@ namespace vcpkg::Commands::DependInfo
|
|||
Util::erase_remove_if(out, [](auto&& info) { return info.depth < 0; });
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to emplace candiate into maybe_target. If that would be inconsistent, return true.
|
||||
// An engaged maybe_target is consistent with candidate if the contained value equals candidate.
|
||||
template<typename T>
|
||||
bool emplace_inconsistent(Optional<T>& maybe_target, const T& candidate)
|
||||
{
|
||||
if (auto target = maybe_target.get())
|
||||
{
|
||||
return *target != candidate;
|
||||
}
|
||||
|
||||
maybe_target.emplace(candidate);
|
||||
return false;
|
||||
}
|
||||
} // unnamed namespace
|
||||
|
||||
const CommandStructure COMMAND_STRUCTURE = {
|
||||
[] { return create_example_string("depend-info sqlite3"); },
|
||||
|
@ -288,15 +236,144 @@ namespace vcpkg::Commands::DependInfo
|
|||
nullptr,
|
||||
};
|
||||
|
||||
ExpectedL<DependInfoStrategy> determine_depend_info_mode(const ParsedArguments& args)
|
||||
{
|
||||
static constexpr StringLiteral OPTION_FORMAT_LIST = "list";
|
||||
static constexpr StringLiteral OPTION_FORMAT_TREE = "tree";
|
||||
static constexpr StringLiteral OPTION_FORMAT_DOT = "dot";
|
||||
static constexpr StringLiteral OPTION_FORMAT_DGML = "dgml";
|
||||
|
||||
auto& settings = args.settings;
|
||||
|
||||
Optional<DependInfoFormat> maybe_format;
|
||||
{
|
||||
auto it = settings.find(OPTION_FORMAT);
|
||||
if (it != settings.end())
|
||||
{
|
||||
auto as_lower = Strings::ascii_to_lowercase(it->second);
|
||||
if (as_lower == OPTION_FORMAT_LIST)
|
||||
{
|
||||
maybe_format.emplace(DependInfoFormat::List);
|
||||
}
|
||||
else if (as_lower == OPTION_FORMAT_TREE)
|
||||
{
|
||||
maybe_format.emplace(DependInfoFormat::Tree);
|
||||
}
|
||||
else if (as_lower == OPTION_FORMAT_DOT)
|
||||
{
|
||||
maybe_format.emplace(DependInfoFormat::Dot);
|
||||
}
|
||||
else if (as_lower == OPTION_FORMAT_DGML)
|
||||
{
|
||||
maybe_format.emplace(DependInfoFormat::Dgml);
|
||||
}
|
||||
else
|
||||
{
|
||||
return msg::format_error(msgCmdDependInfoFormatInvalid, msg::value = it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Util::Sets::contains(args.switches, OPTION_DOT))
|
||||
{
|
||||
if (emplace_inconsistent(maybe_format, DependInfoFormat::Dot))
|
||||
{
|
||||
return msg::format_error(msgCmdDependInfoFormatConflict);
|
||||
}
|
||||
}
|
||||
|
||||
if (Util::Sets::contains(args.switches, OPTION_DGML))
|
||||
{
|
||||
if (emplace_inconsistent(maybe_format, DependInfoFormat::Dgml))
|
||||
{
|
||||
return msg::format_error(msgCmdDependInfoFormatConflict);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr StringLiteral OPTION_SORT_LEXICOGRAPHICAL = "lexicographical";
|
||||
static constexpr StringLiteral OPTION_SORT_TOPOLOGICAL = "topological";
|
||||
static constexpr StringLiteral OPTION_SORT_REVERSE = "reverse";
|
||||
static constexpr StringLiteral OPTION_SORT_TREE = "x-tree";
|
||||
Optional<DependInfoSortMode> maybe_sort_mode;
|
||||
{
|
||||
auto it = settings.find(OPTION_SORT);
|
||||
if (it != settings.end())
|
||||
{
|
||||
auto as_lower = Strings::ascii_to_lowercase(it->second);
|
||||
if (as_lower == OPTION_SORT_LEXICOGRAPHICAL)
|
||||
{
|
||||
maybe_sort_mode.emplace(DependInfoSortMode::Lexicographical);
|
||||
}
|
||||
else if (as_lower == OPTION_SORT_TOPOLOGICAL)
|
||||
{
|
||||
maybe_sort_mode.emplace(DependInfoSortMode::Topological);
|
||||
}
|
||||
else if (as_lower == OPTION_SORT_REVERSE)
|
||||
{
|
||||
maybe_sort_mode.emplace(DependInfoSortMode::ReverseTopological);
|
||||
}
|
||||
else if (as_lower == OPTION_SORT_TREE)
|
||||
{
|
||||
if (emplace_inconsistent(maybe_format, DependInfoFormat::Tree))
|
||||
{
|
||||
return msg::format_error(msgCmdDependInfoXtreeTree);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return msg::format_error(msgInvalidCommandArgSort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DependInfoStrategy result{maybe_sort_mode.value_or(DependInfoSortMode::Topological),
|
||||
maybe_format.value_or(DependInfoFormat::List),
|
||||
INT_MAX,
|
||||
Util::Sets::contains(args.switches, OPTION_SHOW_DEPTH)};
|
||||
|
||||
{
|
||||
auto it = settings.find(OPTION_MAX_RECURSE);
|
||||
if (it != settings.end())
|
||||
{
|
||||
auto maybe_parsed = Strings::strto<int>(it->second);
|
||||
if (auto parsed = maybe_parsed.get())
|
||||
{
|
||||
if (*parsed >= 0)
|
||||
{
|
||||
result.max_depth = *parsed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return msg::format_error(msgOptionMustBeInteger, msg::option = OPTION_MAX_RECURSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.show_depth)
|
||||
{
|
||||
switch (result.format)
|
||||
{
|
||||
case DependInfoFormat::List:
|
||||
case DependInfoFormat::Tree:
|
||||
// ok
|
||||
break;
|
||||
case DependInfoFormat::Dot:
|
||||
case DependInfoFormat::Dgml: return msg::format_error(msgCmdDependInfoShowDepthFormatMismatch);
|
||||
default: Checks::unreachable(VCPKG_LINE_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void perform_and_exit(const VcpkgCmdArguments& args,
|
||||
const VcpkgPaths& paths,
|
||||
Triplet default_triplet,
|
||||
Triplet host_triplet)
|
||||
{
|
||||
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
|
||||
const int max_depth = get_max_depth(options);
|
||||
const SortMode sort_mode = get_sort_mode(options);
|
||||
const bool show_depth = Util::Sets::contains(options.switches, OPTION_SHOW_DEPTH);
|
||||
const auto strategy = determine_depend_info_mode(options).value_or_exit(VCPKG_LINE_INFO);
|
||||
|
||||
bool default_triplet_used = false;
|
||||
const std::vector<FullPackageSpec> specs = Util::fmap(options.command_arguments, [&](auto&& arg) {
|
||||
|
@ -331,26 +408,26 @@ namespace vcpkg::Commands::DependInfo
|
|||
std::vector<const InstallPlanAction*> install_actions =
|
||||
Util::fmap(action_plan.already_installed, [&](const auto& action) { return &action; });
|
||||
for (auto&& action : action_plan.install_actions)
|
||||
install_actions.push_back(&action);
|
||||
|
||||
std::vector<PackageDependInfo> depend_info = extract_depend_info(install_actions, max_depth);
|
||||
|
||||
if (Util::Sets::contains(options.switches, OPTION_DOT) || Util::Sets::contains(options.switches, OPTION_DGML))
|
||||
{
|
||||
const std::vector<const SourceControlFile*> source_control_files =
|
||||
Util::fmap(install_actions, [](const InstallPlanAction* install_action) {
|
||||
const SourceControlFileAndLocation& scfl =
|
||||
install_action->source_control_file_and_location.value_or_exit(VCPKG_LINE_INFO);
|
||||
return const_cast<const SourceControlFile*>(scfl.source_control_file.get());
|
||||
});
|
||||
install_actions.push_back(&action);
|
||||
}
|
||||
|
||||
std::string graph_as_string = create_graph_as_string(options.switches, depend_info);
|
||||
graph_as_string.push_back('\n');
|
||||
msg::write_unlocalized_text_to_stdout(Color::none, graph_as_string);
|
||||
std::vector<PackageDependInfo> depend_info = extract_depend_info(install_actions, strategy.max_depth);
|
||||
|
||||
if (strategy.format == DependInfoFormat::Dot)
|
||||
{
|
||||
msg::write_unlocalized_text_to_stdout(Color::none, create_dot_as_string(depend_info));
|
||||
msg::write_unlocalized_text_to_stdout(Color::none, "\n");
|
||||
Checks::exit_success(VCPKG_LINE_INFO);
|
||||
}
|
||||
|
||||
if (strategy.format == DependInfoFormat::Dgml)
|
||||
{
|
||||
msg::write_unlocalized_text_to_stdout(Color::none, create_dgml_as_string(depend_info));
|
||||
msg::write_unlocalized_text_to_stdout(Color::none, "\n");
|
||||
Checks::exit_success(VCPKG_LINE_INFO);
|
||||
}
|
||||
|
||||
// TODO: Improve this code
|
||||
auto lex = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool {
|
||||
return lhs.package < rhs.package;
|
||||
};
|
||||
|
@ -361,56 +438,64 @@ namespace vcpkg::Commands::DependInfo
|
|||
return lhs.depth < rhs.depth;
|
||||
};
|
||||
|
||||
switch (sort_mode)
|
||||
{
|
||||
case SortMode::Lexicographical: std::sort(std::begin(depend_info), std::end(depend_info), lex); break;
|
||||
case SortMode::ReverseTopological:
|
||||
case SortMode::Treelogical: std::sort(std::begin(depend_info), std::end(depend_info), reverse); break;
|
||||
case SortMode::Topological: std::sort(std::begin(depend_info), std::end(depend_info), topo); break;
|
||||
default: Checks::unreachable(VCPKG_LINE_INFO);
|
||||
}
|
||||
|
||||
if (sort_mode == SortMode::Treelogical)
|
||||
if (strategy.format == DependInfoFormat::Tree)
|
||||
{
|
||||
Util::sort(depend_info, reverse);
|
||||
auto first = depend_info.begin();
|
||||
std::string features = Strings::join(", ", first->features);
|
||||
|
||||
if (show_depth)
|
||||
if (strategy.show_depth)
|
||||
{
|
||||
msg::write_unlocalized_text_to_stdout(Color::error, fmt::format("({})", first->depth));
|
||||
}
|
||||
|
||||
msg::write_unlocalized_text_to_stdout(Color::success, first->package);
|
||||
if (!features.empty())
|
||||
{
|
||||
msg::write_unlocalized_text_to_stdout(Color::warning, "[" + features + "]");
|
||||
}
|
||||
|
||||
msg::write_unlocalized_text_to_stdout(Color::none, "\n");
|
||||
std::set<std::string> printed;
|
||||
std::string prefix_buf;
|
||||
print_dep_tree(prefix_buf, first->package, depend_info, printed);
|
||||
Checks::exit_success(VCPKG_LINE_INFO);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto&& info : depend_info)
|
||||
{
|
||||
if (info.depth >= 0)
|
||||
{
|
||||
const std::string features = Strings::join(", ", info.features);
|
||||
const std::string dependencies = Strings::join(", ", info.dependencies);
|
||||
|
||||
if (show_depth)
|
||||
{
|
||||
msg::write_unlocalized_text_to_stdout(Color::error, fmt::format("({})", info.depth));
|
||||
}
|
||||
msg::write_unlocalized_text_to_stdout(Color::success, info.package);
|
||||
if (!features.empty())
|
||||
{
|
||||
msg::write_unlocalized_text_to_stdout(Color::warning, "[" + features + "]");
|
||||
}
|
||||
msg::write_unlocalized_text_to_stdout(Color::none, ": " + dependencies + "\n");
|
||||
}
|
||||
}
|
||||
if (strategy.format != DependInfoFormat::List)
|
||||
{
|
||||
Checks::unreachable(VCPKG_LINE_INFO);
|
||||
}
|
||||
|
||||
switch (strategy.sort_mode)
|
||||
{
|
||||
case DependInfoSortMode::Lexicographical: Util::sort(depend_info, lex); break;
|
||||
case DependInfoSortMode::ReverseTopological: Util::sort(depend_info, reverse); break;
|
||||
case DependInfoSortMode::Topological: Util::sort(depend_info, topo); break;
|
||||
default: Checks::unreachable(VCPKG_LINE_INFO);
|
||||
}
|
||||
|
||||
for (auto&& info : depend_info)
|
||||
{
|
||||
if (info.depth < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strategy.show_depth)
|
||||
{
|
||||
msg::write_unlocalized_text_to_stdout(Color::error, fmt::format("({})", info.depth));
|
||||
}
|
||||
|
||||
msg::write_unlocalized_text_to_stdout(Color::success, info.package);
|
||||
if (!info.features.empty())
|
||||
{
|
||||
msg::write_unlocalized_text_to_stdout(Color::warning, "[" + Strings::join(", ", info.features) + "]");
|
||||
}
|
||||
|
||||
msg::write_unlocalized_text_to_stdout(Color::none, ": " + Strings::join(", ", info.dependencies) + "\n");
|
||||
}
|
||||
|
||||
Checks::exit_success(VCPKG_LINE_INFO);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
"_UnrecognizedCommand$.comment": "\n'${p0}' (aka 'commandline.inputs[0]') is a parameter of type 'string'\n",
|
||||
"Use$ToGetHelp": "Use ${p0} to get help",
|
||||
"_Use$ToGetHelp.comment": "\n'${p0}' is a parameter of type 'string'\n",
|
||||
"FatalTheRootFolder$CanNotBeCreated": "Fatal: The root folder '${p0}' can not be created",
|
||||
"_FatalTheRootFolder$CanNotBeCreated.comment": "\n'${p0}' (aka 'this.homeFolder.fsPath') is a parameter of type 'string'\n",
|
||||
"FatalTheGlobalConfigurationFile$CanNotBeCreated": "Fatal: The global configuration file '${p0}' can not be created",
|
||||
"_FatalTheGlobalConfigurationFile$CanNotBeCreated.comment": "\n'${p0}' (aka 'this.globalConfig.fsPath') is a parameter of type 'string'\n",
|
||||
"FatalTheRootFolder$CannotBeCreated": "Fatal: The root folder '${p0}' cannot be created",
|
||||
"_FatalTheRootFolder$CannotBeCreated.comment": "\n'${p0}' (aka 'this.homeFolder.fsPath') is a parameter of type 'string'\n",
|
||||
"FatalTheGlobalConfigurationFile$CannotBeCreated": "Fatal: The global configuration file '${p0}' cannot be created",
|
||||
"_FatalTheGlobalConfigurationFile$CannotBeCreated.comment": "\n'${p0}' (aka 'this.globalConfig.fsPath') is a parameter of type 'string'\n",
|
||||
"VCPKGCOMMANDWasNotSet": "VCPKG_COMMAND was not set",
|
||||
"RunningVcpkgInternallyReturnedANonzeroExitCode$": "Running vcpkg internally returned a nonzero exit code: ${p0}",
|
||||
"_RunningVcpkgInternallyReturnedANonzeroExitCode$.comment": "\n'${p0}' is a parameter of type 'number'\n",
|
||||
|
|
|
@ -55,7 +55,7 @@ const mergeConflictMarkerLength = 7;
|
|||
* is 1 and the character offset of b is 3 since `𐐀` is represented using two code
|
||||
* units in UTF-16.
|
||||
*
|
||||
* Positions are line end character agnostic. So you can not specify a position that
|
||||
* Positions are line end character agnostic. So you cannot specify a position that
|
||||
* denotes `\r|\n` or `\n|` where `|` represents the character offset.
|
||||
*/
|
||||
export interface Position {
|
||||
|
|
|
@ -162,7 +162,7 @@ export class Session {
|
|||
this.channels.debug(error?.message);
|
||||
}
|
||||
// check if it got made, because at an absolute minimum, we need a folder, so failing this is catastrophic.
|
||||
strict.ok(await this.fileSystem.isDirectory(this.homeFolder), i`Fatal: The root folder '${this.homeFolder.fsPath}' can not be created`);
|
||||
strict.ok(await this.fileSystem.isDirectory(this.homeFolder), i`Fatal: The root folder '${this.homeFolder.fsPath}' cannot be created`);
|
||||
}
|
||||
|
||||
if (!await this.fileSystem.isFile(this.globalConfig)) {
|
||||
|
@ -172,7 +172,7 @@ export class Session {
|
|||
// if this throws, let it
|
||||
}
|
||||
// check if it got made, because at an absolute minimum, we need the config file, so failing this is catastrophic.
|
||||
strict.ok(await this.fileSystem.isFile(this.globalConfig), i`Fatal: The global configuration file '${this.globalConfig.fsPath}' can not be created`);
|
||||
strict.ok(await this.fileSystem.isFile(this.globalConfig), i`Fatal: The global configuration file '${this.globalConfig.fsPath}' cannot be created`);
|
||||
}
|
||||
|
||||
// got past the checks, let's load the configuration.
|
||||
|
|
Загрузка…
Ссылка в новой задаче