[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
{
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
{
static std::string to_string(const std::string& name, const Triplet& triplet);

View File

@ -5,18 +5,23 @@
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 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); }
// Exit the tool successfully.
[[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);
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,
const char* errorMessageTemplate,
const Arg1 errorMessageArg1,

View File

@ -13,52 +13,16 @@ namespace vcpkg
ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string,
const Triplet& default_triplet)
{
auto pos = spec_as_string.find(':');
auto pos_l_bracket = spec_as_string.find('[');
auto pos_r_bracket = spec_as_string.find(']');
FullPackageSpec f;
if (pos == std::string::npos && pos_l_bracket == std::string::npos)
auto res = ParsedSpecifier::from_string(spec_as_string);
if (auto p = res.get())
{
f.package_spec =
PackageSpec::from_name_and_triplet(spec_as_string, default_triplet).value_or_exit(VCPKG_LINE_INFO);
return f;
FullPackageSpec fspec;
Triplet t = p->triplet.empty() ? default_triplet : Triplet::from_canonical_name(p->triplet);
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)
{
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;
return res.error();
}
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); }
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 "PackageSpec.h"
#include "SourceParagraph.h"
#include "Triplet.h"
#include "vcpkg_Checks.h"
@ -28,7 +29,11 @@ namespace vcpkg
static span<const std::string> get_list_of_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;
@ -154,21 +159,20 @@ namespace vcpkg
Features parse_feature_list(const std::string& name)
{
Features f;
int end = (int)name.find(']');
if (end != std::string::npos)
auto maybe_spec = ParsedSpecifier::from_string(name);
if (auto spec = maybe_spec.get())
{
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);
f.name = name.substr(0, start);
f.features = parse_comma_list(feature_name_list);
Features f;
f.name = spec->name;
f.features = spec->features;
return f;
}
else
{
f.name = name;
}
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)

View File

@ -385,42 +385,53 @@ namespace UnitTest1
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 =
vcpkg::FullPackageSpec::from_string("zlib", vcpkg::Triplet::X86_WINDOWS);
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, spec.error());
Assert::AreEqual("zlib", spec.get()->package_spec.name().c_str());
Assert::AreEqual(vcpkg::Triplet::X86_WINDOWS.canonical_name(),
spec.get()->package_spec.triplet().canonical_name());
auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib");
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
auto spec = maybe_spec.get();
Assert::AreEqual("zlib", spec->name.c_str());
Assert::AreEqual(size_t(0), spec->features.size());
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 =
vcpkg::FullPackageSpec::from_string("zlib:x64-uwp", vcpkg::Triplet::X86_WINDOWS);
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, spec.error());
Assert::AreEqual("zlib", spec.get()->package_spec.name().c_str());
Assert::AreEqual(vcpkg::Triplet::X64_UWP.canonical_name(),
spec.get()->package_spec.triplet().canonical_name());
auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib:x64-uwp");
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, maybe_spec.error());
auto spec = maybe_spec.get();
Assert::AreEqual("zlib", spec->name.c_str());
Assert::AreEqual("x64-uwp", spec->triplet.c_str());
}
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);
}
TEST_METHOD(package_spec_feature_parse_with_arch)
TEST_METHOD(parsed_specifier_from_string_with_feature)
{
vcpkg::ExpectedT<vcpkg::FullPackageSpec, vcpkg::PackageSpecParseResult> spec =
vcpkg::FullPackageSpec::from_string("zlib[feature]:x64-uwp", vcpkg::Triplet::X86_WINDOWS);
Assert::AreEqual(vcpkg::PackageSpecParseResult::SUCCESS, spec.error());
Assert::AreEqual("zlib", spec.get()->package_spec.name().c_str());
Assert::IsTrue(spec.get()->features.size() == 1);
Assert::AreEqual("feature", spec.get()->features.front().c_str());
Assert::AreEqual(vcpkg::Triplet::X64_UWP.canonical_name(),
spec.get()->package_spec.triplet().canonical_name());
auto maybe_spec = vcpkg::ParsedSpecifier::from_string("zlib[feature]:x64-uwp");
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() == 1);
Assert::AreEqual("feature", spec->features.front().c_str());
Assert::AreEqual("x64-uwp", spec->triplet.c_str());
}
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)

View File

@ -363,23 +363,25 @@ namespace vcpkg::Dependencies
std::vector<FeatureSpec> f_specs;
for (auto&& depend : depends)
{
int end = (int)depend.find(']');
if (end != std::string::npos)
auto maybe_spec = ParsedSpecifier::from_string(depend);
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);
auto package_name = depend.substr(0, start);
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};
f_specs.emplace_back(std::move(feature_spec));
for (auto&& feature : spec->features)
f_specs.push_back(FeatureSpec{pspec, feature});
if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, ""});
}
else
{
auto p_spec = PackageSpec::from_name_and_triplet(depend, triplet).value_or_exit(VCPKG_LINE_INFO);
auto feature_spec = FeatureSpec{p_spec, ""};
f_specs.emplace_back(std::move(feature_spec));
Checks::exit_with_message(
VCPKG_LINE_INFO, "error while parsing feature list: %s: %s", to_string(maybe_spec.error()), depend);
}
}
return f_specs;