diff options
Diffstat (limited to 'toolsrc/src/vcpkg/packagespec.cpp')
| -rw-r--r-- | toolsrc/src/vcpkg/packagespec.cpp | 274 |
1 files changed, 145 insertions, 129 deletions
diff --git a/toolsrc/src/vcpkg/packagespec.cpp b/toolsrc/src/vcpkg/packagespec.cpp index ea25f57e1..702f7aad3 100644 --- a/toolsrc/src/vcpkg/packagespec.cpp +++ b/toolsrc/src/vcpkg/packagespec.cpp @@ -3,18 +3,11 @@ #include <vcpkg/base/checks.h> #include <vcpkg/base/util.h> #include <vcpkg/packagespec.h> -#include <vcpkg/packagespecparseresult.h> +#include <vcpkg/paragraphparser.h> #include <vcpkg/parse.h> -using vcpkg::Parse::parse_comma_list; - namespace vcpkg { - static bool is_valid_package_spec_char(char c) - { - return (c == '-') || isdigit(c) || (isalpha(c) && islower(c)) || (c == '[') || (c == ']'); - } - std::string FeatureSpec::to_string() const { std::string ret; @@ -27,38 +20,6 @@ namespace vcpkg Strings::append(out, name(), '[', feature(), "]:", triplet()); } - std::vector<FeatureSpec> FeatureSpec::from_strings_and_triplet(const std::vector<std::string>& depends, - const Triplet& triplet) - { - std::vector<FeatureSpec> f_specs; - for (auto&& depend : depends) - { - auto maybe_spec = ParsedSpecifier::from_string(depend); - if (auto spec = maybe_spec.get()) - { - 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); - - for (auto&& feature : spec->features) - f_specs.push_back(FeatureSpec{pspec, feature}); - - if (spec->features.empty()) f_specs.push_back(FeatureSpec{pspec, "core"}); - } - else - { - Checks::exit_with_message(VCPKG_LINE_INFO, - "error while parsing feature list: %s: %s", - vcpkg::to_string(maybe_spec.error()), - depend); - } - } - return f_specs; - } - std::vector<FeatureSpec> FullPackageSpec::to_feature_specs(const std::vector<std::string>& default_features, const std::vector<std::string>& all_features) const { @@ -99,57 +60,26 @@ namespace vcpkg return feature_specs; } - ExpectedT<FullPackageSpec, PackageSpecParseResult> FullPackageSpec::from_string(const std::string& spec_as_string, - const Triplet& default_triplet) - { - auto res = ParsedSpecifier::from_string(spec_as_string); - if (auto p = res.get()) - { - FullPackageSpec fspec; - Triplet t = p->triplet.empty() ? default_triplet : Triplet::from_canonical_name(std::move(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; - } - return res.error(); - } - - ExpectedT<PackageSpec, PackageSpecParseResult> PackageSpec::from_name_and_triplet(const std::string& name, - const Triplet& triplet) + ExpectedS<FullPackageSpec> FullPackageSpec::from_string(const std::string& spec_as_string, Triplet default_triplet) { - if (Util::find_if_not(name, is_valid_package_spec_char) != name.end()) - { - return PackageSpecParseResult::INVALID_CHARACTERS; - } - - PackageSpec p; - p.m_name = name; - p.m_triplet = triplet; - return p; + return parse_qualified_specifier(spec_as_string) + .then([&](ParsedQualifiedSpecifier&& p) -> ExpectedS<FullPackageSpec> { + if (p.qualifier) return "Error: qualifier not allowed in this context: " + spec_as_string + "\n"; + auto triplet = p.triplet ? Triplet::from_canonical_name(std::move(*p.triplet.get())) : default_triplet; + return FullPackageSpec({p.name, triplet}, p.features.value_or({})); + }); } - std::vector<PackageSpec> PackageSpec::to_package_specs(const std::vector<std::string>& ports, - const Triplet& triplet) + std::vector<PackageSpec> PackageSpec::to_package_specs(const std::vector<std::string>& ports, Triplet triplet) { return Util::fmap(ports, [&](const std::string& spec_as_string) -> PackageSpec { - auto maybe_spec = PackageSpec::from_name_and_triplet(spec_as_string, triplet); - if (auto spec = maybe_spec.get()) - { - return std::move(*spec); - } - - const PackageSpecParseResult error_type = maybe_spec.error(); - Checks::exit_with_message(VCPKG_LINE_INFO, - "Invalid package: %s\n" - "%s", - spec_as_string, - vcpkg::to_string(error_type)); + return {spec_as_string, triplet}; }); } const std::string& PackageSpec::name() const { return this->m_name; } - const Triplet& PackageSpec::triplet() const { return this->m_triplet; } + Triplet PackageSpec::triplet() const { return this->m_triplet; } std::string PackageSpec::dir() const { return Strings::format("%s_%s", this->m_name, this->m_triplet); } @@ -163,69 +93,155 @@ namespace vcpkg bool operator!=(const PackageSpec& left, const PackageSpec& right) { return !(left == right); } - ExpectedT<ParsedSpecifier, PackageSpecParseResult> ParsedSpecifier::from_string(const std::string& input) + ExpectedS<Features> Features::from_string(const std::string& name) + { + return parse_qualified_specifier(name).then([&](ParsedQualifiedSpecifier&& pqs) -> ExpectedS<Features> { + if (pqs.triplet) return "Error: triplet not allowed in this context: " + name + "\n"; + if (pqs.qualifier) return "Error: qualifier not allowed in this context: " + name + "\n"; + return Features{pqs.name, pqs.features.value_or({})}; + }); + } + + static bool is_package_name_char(char ch) { - auto pos = input.find(':'); - auto pos_l_bracket = input.find('['); - auto pos_r_bracket = input.find(']'); + return Parse::ParserBase::is_lower_alpha(ch) || Parse::ParserBase::is_ascii_digit(ch) || ch == '-'; + } + + static bool is_feature_name_char(char ch) { + // TODO: we do not intend underscores to be valid, however there is currently a feature using them (libwebp[vwebp_sdl]). + // TODO: we need to rename this feature, then remove underscores from this list. + return is_package_name_char(ch) || ch == '_'; + } + + ExpectedS<ParsedQualifiedSpecifier> parse_qualified_specifier(CStringView input) + { + Parse::ParserBase parser; + parser.init(input, "<unknown>"); + auto maybe_pqs = parse_qualified_specifier(parser); + if (!parser.at_eof()) parser.add_error("expected eof"); + if (auto e = parser.get_error()) return e->format(); + return std::move(maybe_pqs).value_or_exit(VCPKG_LINE_INFO); + } - ParsedSpecifier f; - if (pos == std::string::npos && pos_l_bracket == std::string::npos) + Optional<std::string> parse_feature_name(Parse::ParserBase& parser) + { + using Parse::ParserBase; + auto ret = parser.match_zero_or_more(is_feature_name_char).to_string(); + auto ch = parser.cur(); + if (ParserBase::is_upper_alpha(ch) || ch == '_') { - f.name = input; - return f; + parser.add_error("invalid character in feature name (must be lowercase, digits, '-')"); + return nullopt; } - else if (pos == std::string::npos) + if (ret.empty()) { - 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; + parser.add_error("expected feature name (must be lowercase, digits, '-')"); + return nullopt; } - else if (pos_l_bracket == std::string::npos && pos_r_bracket == std::string::npos) + return ret; + } + Optional<std::string> parse_package_name(Parse::ParserBase& parser) + { + using Parse::ParserBase; + auto ret = parser.match_zero_or_more(is_package_name_char).to_string(); + auto ch = parser.cur(); + if (ParserBase::is_upper_alpha(ch) || ch == '_') + { + parser.add_error("invalid character in package name (must be lowercase, digits, '-')"); + return nullopt; + } + if (ret.empty()) { - const std::string name = input.substr(0, pos); - f.triplet = input.substr(pos + 1); - f.name = name; + parser.add_error("expected package name (must be lowercase, digits, '-')"); + return nullopt; } + return ret; + } + + Optional<ParsedQualifiedSpecifier> parse_qualified_specifier(Parse::ParserBase& parser) + { + using Parse::ParserBase; + ParsedQualifiedSpecifier ret; + auto name = parse_package_name(parser); + if (auto n = name.get()) + ret.name = std::move(*n); else + return nullopt; + auto ch = parser.cur(); + if (ch == '[') { - if (pos_r_bracket == std::string::npos || pos_l_bracket >= pos_r_bracket) + std::vector<std::string> features; + do { - 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; + parser.next(); + parser.skip_tabs_spaces(); + if (parser.cur() == '*') + { + features.push_back("*"); + parser.next(); + } + else + { + auto feature = parse_feature_name(parser); + if (auto f = feature.get()) + features.push_back(std::move(*f)); + else + return nullopt; + } + auto skipped_space = parser.skip_tabs_spaces(); + ch = parser.cur(); + if (ch == ']') + { + ch = parser.next(); + break; + } + else if (ch == ',') + { + continue; + } + else + { + if (skipped_space.size() > 0 || Parse::ParserBase::is_lineend(parser.cur())) + parser.add_error("expected ',' or ']' in feature list"); + else + parser.add_error("invalid character in feature name (must be lowercase, digits, '-', or '*')"); + return nullopt; + } + } while (true); + ret.features = std::move(features); } - - auto pos2 = input.find(':', pos + 1); - if (pos2 != std::string::npos) + if (ch == ':') { - return PackageSpecParseResult::TOO_MANY_COLONS; + parser.next(); + ret.triplet = parser.match_zero_or_more(is_package_name_char).to_string(); + if (ret.triplet.get()->empty()) + { + parser.add_error("expected triplet name (must be lowercase, digits, '-')"); + return nullopt; + } } - return f; - } - - ExpectedT<Features, PackageSpecParseResult> Features::from_string(const std::string& name) - { - auto maybe_spec = ParsedSpecifier::from_string(name); - if (auto spec = maybe_spec.get()) + parser.skip_tabs_spaces(); + ch = parser.cur(); + if (ch == '(') { - Checks::check_exit( - VCPKG_LINE_INFO, spec->triplet.empty(), "error: triplet not allowed in specifier: %s", name); - - Features f; - f.name = spec->name; - f.features = spec->features; - return f; + auto loc = parser.cur_loc(); + int depth = 1; + while (depth > 0 && (ch = parser.next()) != 0) + { + if (ch == '(') ++depth; + if (ch == ')') --depth; + } + if (depth > 0) + { + parser.add_error("unmatched open braces in qualifier", loc); + return nullopt; + } + ret.qualifier = StringView(loc.it + 1, parser.it()).to_string(); + parser.next(); } - - return maybe_spec.error(); + // This makes the behavior of the parser more consistent -- otherwise, it will skip tabs and spaces only if + // there isn't a qualifier. + parser.skip_tabs_spaces(); + return ret; } } |
