[vcpkg] Consolidate specifier parsing

This commit is contained in:
Robert Schumacher 2017-08-18 20:32:35 -07:00
parent aab0173509
commit 4d34488649
6 changed files with 139 additions and 95 deletions

View File

@ -6,6 +6,15 @@
namespace vcpkg namespace vcpkg
{ {
struct ParsedSpecifier
{
std::string name;
std::vector<std::string> features;
std::string triplet;
static ExpectedT<ParsedSpecifier, PackageSpecParseResult> from_string(const std::string& input);
};
struct PackageSpec struct PackageSpec
{ {
static std::string to_string(const std::string& name, const Triplet& triplet); static std::string to_string(const std::string& name, const Triplet& triplet);

View File

@ -5,18 +5,23 @@
namespace vcpkg::Checks namespace vcpkg::Checks
{ {
// Indicate that an internal error has occurred and exit the tool. This should be used when invariants have been
// broken.
[[noreturn]] void unreachable(const LineInfo& line_info); [[noreturn]] void unreachable(const LineInfo& line_info);
[[noreturn]] void exit_with_code(const LineInfo& line_info, const int exit_code); [[noreturn]] void exit_with_code(const LineInfo& line_info, const int exit_code);
// Exit the tool without an error message.
[[noreturn]] inline void exit_fail(const LineInfo& line_info) { exit_with_code(line_info, EXIT_FAILURE); } [[noreturn]] inline void exit_fail(const LineInfo& line_info) { exit_with_code(line_info, EXIT_FAILURE); }
// Exit the tool successfully.
[[noreturn]] inline void exit_success(const LineInfo& line_info) { exit_with_code(line_info, EXIT_SUCCESS); } [[noreturn]] inline void exit_success(const LineInfo& line_info) { exit_with_code(line_info, EXIT_SUCCESS); }
// Part of the reason these exist is to not include extra headers in this one to avoid circular #includes. // Display an error message to the user and exit the tool.
[[noreturn]] void exit_with_message(const LineInfo& line_info, const CStringView errorMessage); [[noreturn]] void exit_with_message(const LineInfo& line_info, const CStringView errorMessage);
template<class Arg1, class... Args> template<class Arg1, class... Args>
// Display an error message to the user and exit the tool.
[[noreturn]] void exit_with_message(const LineInfo& line_info, [[noreturn]] void exit_with_message(const LineInfo& line_info,
const char* errorMessageTemplate, const char* errorMessageTemplate,
const Arg1 errorMessageArg1, const Arg1 errorMessageArg1,

View File

@ -13,52 +13,16 @@ namespace vcpkg
ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string, ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string,
const Triplet& default_triplet) const Triplet& default_triplet)
{ {
auto pos = spec_as_string.find(':'); auto res = ParsedSpecifier::from_string(spec_as_string);
auto pos_l_bracket = spec_as_string.find('['); if (auto p = res.get())
auto pos_r_bracket = spec_as_string.find(']');
FullPackageSpec f;
if (pos == std::string::npos && pos_l_bracket == std::string::npos)
{ {
f.package_spec = FullPackageSpec fspec;
PackageSpec::from_name_and_triplet(spec_as_string, default_triplet).value_or_exit(VCPKG_LINE_INFO); Triplet t = p->triplet.empty() ? default_triplet : Triplet::from_canonical_name(p->triplet);
return f; fspec.package_spec = PackageSpec::from_name_and_triplet(p->name, t).value_or_exit(VCPKG_LINE_INFO);
fspec.features = std::move(p->features);
return fspec;
} }
else if (pos == std::string::npos) return res.error();
{
if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
{
return PackageSpecParseResult::INVALID_CHARACTERS;
}
const std::string name = spec_as_string.substr(0, pos_l_bracket);
f.package_spec = PackageSpec::from_name_and_triplet(name, default_triplet).value_or_exit(VCPKG_LINE_INFO);
f.features = parse_comma_list(spec_as_string.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
return f;
}
else if (pos_l_bracket == std::string::npos && pos_r_bracket == std::string::npos)
{
const std::string name = spec_as_string.substr(0, pos);
const Triplet triplet = Triplet::from_canonical_name(spec_as_string.substr(pos + 1));
f.package_spec = PackageSpec::from_name_and_triplet(name, triplet).value_or_exit(VCPKG_LINE_INFO);
}
else
{
if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
{
return PackageSpecParseResult::INVALID_CHARACTERS;
}
const std::string name = spec_as_string.substr(0, pos_l_bracket);
f.features = parse_comma_list(spec_as_string.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
const Triplet triplet = Triplet::from_canonical_name(spec_as_string.substr(pos + 1));
f.package_spec = PackageSpec::from_name_and_triplet(name, triplet).value_or_exit(VCPKG_LINE_INFO);
}
auto pos2 = spec_as_string.find(':', pos + 1);
if (pos2 != std::string::npos)
{
return PackageSpecParseResult::TOO_MANY_COLONS;
}
return f;
} }
ExpectedT<PackageSpec, PackageSpecParseResult> PackageSpec::from_name_and_triplet(const std::string& name, ExpectedT<PackageSpec, PackageSpecParseResult> PackageSpec::from_name_and_triplet(const std::string& name,
@ -93,4 +57,53 @@ namespace vcpkg
} }
bool operator!=(const PackageSpec& left, const PackageSpec& right) { return !(left == right); } bool operator!=(const PackageSpec& left, const PackageSpec& right) { return !(left == right); }
ExpectedT<ParsedSpecifier, PackageSpecParseResult> ParsedSpecifier::from_string(const std::string& input)
{
auto pos = input.find(':');
auto pos_l_bracket = input.find('[');
auto pos_r_bracket = input.find(']');
ParsedSpecifier f;
if (pos == std::string::npos && pos_l_bracket == std::string::npos)
{
f.name = input;
return f;
}
else if (pos == std::string::npos)
{
if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
{
return PackageSpecParseResult::INVALID_CHARACTERS;
}
const std::string name = input.substr(0, pos_l_bracket);
f.name = name;
f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
return f;
}
else if (pos_l_bracket == std::string::npos && pos_r_bracket == std::string::npos)
{
const std::string name = input.substr(0, pos);
f.triplet = input.substr(pos + 1);
f.name = name;
}
else
{
if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket)
{
return PackageSpecParseResult::INVALID_CHARACTERS;
}
const std::string name = input.substr(0, pos_l_bracket);
f.features = parse_comma_list(input.substr(pos_l_bracket + 1, pos_r_bracket - pos_l_bracket - 1));
f.triplet = input.substr(pos + 1);
f.name = name;
}
auto pos2 = input.find(':', pos + 1);
if (pos2 != std::string::npos)
{
return PackageSpecParseResult::TOO_MANY_COLONS;
}
return f;
}
} }

View File

@ -1,5 +1,6 @@
#include "pch.h" #include "pch.h"
#include "PackageSpec.h"
#include "SourceParagraph.h" #include "SourceParagraph.h"
#include "Triplet.h" #include "Triplet.h"
#include "vcpkg_Checks.h" #include "vcpkg_Checks.h"
@ -28,7 +29,11 @@ namespace vcpkg
static span<const std::string> get_list_of_valid_fields() static span<const std::string> get_list_of_valid_fields()
{ {
static const std::string valid_fields[] = { static const std::string valid_fields[] = {
Fields::SOURCE, Fields::VERSION, Fields::DESCRIPTION, Fields::MAINTAINER, Fields::BUILD_DEPENDS, Fields::SOURCE,
Fields::VERSION,
Fields::DESCRIPTION,
Fields::MAINTAINER,
Fields::BUILD_DEPENDS,
}; };
return valid_fields; return valid_fields;
@ -154,23 +159,22 @@ namespace vcpkg
Features parse_feature_list(const std::string& name) Features parse_feature_list(const std::string& name)
{ {
Features f; auto maybe_spec = ParsedSpecifier::from_string(name);
int end = (int)name.find(']'); if (auto spec = maybe_spec.get())
if (end != std::string::npos)
{ {
int start = (int)name.find('['); Checks::check_exit(
VCPKG_LINE_INFO, spec->triplet.empty(), "error: triplet not allowed in specifier: %s", name);
auto feature_name_list = name.substr(start + 1, end - start - 1); Features f;
f.name = name.substr(0, start); f.name = spec->name;
f.features = parse_comma_list(feature_name_list); f.features = spec->features;
}
else
{
f.name = name;
}
return f; return f;
} }
Checks::exit_with_message(
VCPKG_LINE_INFO, "error while parsing feature list: %s: %s", to_string(maybe_spec.error()), name);
}
Dependency Dependency::parse_dependency(std::string name, std::string qualifier) Dependency Dependency::parse_dependency(std::string name, std::string qualifier)
{ {
Dependency dep; Dependency dep;

View File

@ -385,42 +385,53 @@ namespace UnitTest1
Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str()); Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str());
} }
TEST_METHOD(package_spec_parse) TEST_METHOD(parsed_specifier_from_string)
{ {
vcpkg::ExpectedT<vcpkg::FullPackageSpec, vcpkg::PackageSpecParseResult> spec = auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib");
vcpkg::FullPackageSpec::from_string("zlib", vcpkg::Triplet::X86_WINDOWS); Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, spec.error()); auto spec = maybe_spec.get();
Assert::AreEqual("zlib", spec.get()->package_spec.name().c_str()); Assert::AreEqual("zlib", spec->name.c_str());
Assert::AreEqual(vcpkg::Triplet::X86_WINDOWS.canonical_name(), Assert::AreEqual(size_t(0), spec->features.size());
spec.get()->package_spec.triplet().canonical_name()); Assert::AreEqual("", spec->triplet.c_str());
} }
TEST_METHOD(package_spec_parse_with_arch) TEST_METHOD(parsed_specifier_from_string_with_triplet)
{ {
vcpkg::ExpectedT<vcpkg::FullPackageSpec, vcpkg::PackageSpecParseResult> spec = auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp");
vcpkg::FullPackageSpec::from_string("zlib:x64-uwp", vcpkg::Triplet::X86_WINDOWS); Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, spec.error()); auto spec = maybe_spec.get();
Assert::AreEqual("zlib", spec.get()->package_spec.name().c_str()); Assert::AreEqual("zlib", spec->name.c_str());
Assert::AreEqual(vcpkg::Triplet::X64_UWP.canonical_name(), Assert::AreEqual("x64-uwp", spec->triplet.c_str());
spec.get()->package_spec.triplet().canonical_name());
} }
TEST_METHOD(package_spec_parse_with_multiple_colon) TEST_METHOD(parsed_specifier_from_string_with_colons)
{ {
auto ec = vcpkg::FullPackageSpec::from_string("zlib:x86-uwp:", vcpkg::Triplet::X86_WINDOWS).error(); auto ec = vcpkg::ParsedSpecifier::from_string("zlib:x86-uwp:").error();
Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, ec); Assert::AreEqual(vcpkg::PackageSpecParseResult::TOO_MANY_COLONS, ec);
} }
TEST_METHOD(package_spec_feature_parse_with_arch) TEST_METHOD(parsed_specifier_from_string_with_feature)
{ {
vcpkg::ExpectedT<vcpkg::FullPackageSpec, vcpkg::PackageSpecParseResult> spec = auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp");
vcpkg::FullPackageSpec::from_string("zlib[feature]:x64-uwp", vcpkg::Triplet::X86_WINDOWS); Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, spec.error()); auto spec = maybe_spec.get();
Assert::AreEqual("zlib", spec.get()->package_spec.name().c_str()); Assert::AreEqual("zlib", spec->name.c_str());
Assert::IsTrue(spec.get()->features.size() == 1); Assert::IsTrue(spec->features.size() == 1);
Assert::AreEqual("feature", spec.get()->features.front().c_str()); Assert::AreEqual("feature", spec->features.front().c_str());
Assert::AreEqual(vcpkg::Triplet::X64_UWP.canonical_name(), Assert::AreEqual("x64-uwp", spec->triplet.c_str());
spec.get()->package_spec.triplet().canonical_name()); }
TEST_METHOD(parsed_specifier_from_string_with_many_features)
{
auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[0, 1,2]");
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
auto spec = maybe_spec.get();
Assert::AreEqual("zlib", spec->name.c_str());
Assert::IsTrue(spec->features.size() == 3);
Assert::AreEqual("0", spec->features[0].c_str());
Assert::AreEqual("1", spec->features[1].c_str());
Assert::AreEqual("2", spec->features[2].c_str());
Assert::AreEqual("", spec->triplet.c_str());
} }
TEST_METHOD(utf8_to_utf16) TEST_METHOD(utf8_to_utf16)

View File

@ -363,23 +363,25 @@ namespace vcpkg::Dependencies
std::vector<FeatureSpec> f_specs; std::vector<FeatureSpec> f_specs;
for (auto&& depend : depends) for (auto&& depend : depends)
{ {
int end = (int)depend.find(']'); auto maybe_spec = ParsedSpecifier::from_string(depend);
if (end != std::string::npos) if (auto spec = maybe_spec.get())
{ {
int start = (int)depend.find('['); Checks::check_exit(VCPKG_LINE_INFO,
spec->triplet.empty(),
"error: triplets cannot currently be specified in this context: %s",
depend);
PackageSpec pspec =
PackageSpec::from_name_and_triplet(spec->name, triplet).value_or_exit(VCPKG_LINE_INFO);
auto feature_name = depend.substr(start + 1, end - start - 1); for (auto&& feature : spec->features)
auto package_name = depend.substr(0, start); f_specs.push_back(FeatureSpec{pspec, feature});
auto p_spec = PackageSpec::from_name_and_triplet(package_name, triplet).value_or_exit(VCPKG_LINE_INFO);
auto feature_spec = FeatureSpec{p_spec, feature_name}; if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, ""});
f_specs.emplace_back(std::move(feature_spec));
} }
else else
{ {
auto p_spec = PackageSpec::from_name_and_triplet(depend, triplet).value_or_exit(VCPKG_LINE_INFO); Checks::exit_with_message(
VCPKG_LINE_INFO, "error while parsing feature list: %s: %s", to_string(maybe_spec.error()), depend);
auto feature_spec = FeatureSpec{p_spec, ""};
f_specs.emplace_back(std::move(feature_spec));
} }
} }
return f_specs; return f_specs;