[vcpkg, xml] Parse with string-search instead of regex

Resolves issue with inconsistent regex behavior between platforms.
For example [/s/S]*? does not properly match on Clang. String-searching is
also generally more performant.
This commit is contained in:
Alexander Karatarakis 2018-04-16 16:14:50 -07:00
parent 4e48a1c235
commit a9b1541fd6
1 changed files with 40 additions and 43 deletions

View File

@ -41,6 +41,22 @@ namespace vcpkg::Commands::Fetch
return result;
}
static Optional<std::string> extract_string_between_delimiters(const std::string& input,
const std::string& left_delim,
const std::string& right_delim,
const size_t& starting_offset = 0)
{
const size_t from = input.find(left_delim, starting_offset);
if (from == std::string::npos) return nullopt;
const size_t substring_start = from + left_delim.length();
const size_t to = input.find(right_delim, substring_start);
if (from == std::string::npos) return nullopt;
return input.substr(substring_start, to - substring_start);
}
static ToolData parse_tool_data_from_xml(const VcpkgPaths& paths, const std::string& tool)
{
#if defined(_WIN32)
@ -57,33 +73,21 @@ namespace vcpkg::Commands::Fetch
static const std::string XML_VERSION = "2";
static const fs::path XML_PATH = paths.scripts / "vcpkgTools.xml";
const auto maybe_get_string_inside_tags = [](const std::string& input,
const std::regex& regex) -> Optional<std::string> {
std::smatch match;
const bool has_match = std::regex_search(input.cbegin(), input.cend(), match, regex);
if (!has_match) return nullopt;
return match[1];
};
const auto get_string_inside_tags =
[](const std::string& input, const std::regex& regex, const std::string& tag_name) -> std::string {
std::smatch match;
const bool has_match = std::regex_search(input.cbegin(), input.cend(), match, regex);
Checks::check_exit(
VCPKG_LINE_INFO, has_match, "Could not find tag <%s> in %s", tag_name, XML_PATH.generic_string());
[](const std::string& input, const std::string& left_delim, const std::string& right_delim) -> std::string {
Optional<std::string> result = extract_string_between_delimiters(input, left_delim, right_delim);
Checks::check_exit(VCPKG_LINE_INFO,
result.has_value(),
"Could not find tag <%s>.*<%s> in %s",
left_delim,
right_delim,
XML_PATH.generic_string());
return match[1];
return *result.get();
};
static const std::regex XML_VERSION_REGEX{R"###(<tools[\s]+version="([^"]+)">)###"};
static const std::string XML = paths.get_filesystem().read_contents(XML_PATH).value_or_exit(VCPKG_LINE_INFO);
static const std::regex VERSION_REGEX{R"###(<version>([\s\S]*?)</version>)###"};
static const std::regex EXE_RELATIVE_PATH_REGEX{
Strings::format(R"###(<exeRelativePath>([\s\S]*?)</exeRelativePath>)###")};
static const std::regex ARCHIVE_NAME_REGEX{Strings::format(R"###(<archiveName>([\s\S]*?)</archiveName>)###")};
static const std::regex URL_REGEX{Strings::format(R"###(<url>([\s\S]*?)</url>)###")};
static const std::regex SHA512_REGEX{Strings::format(R"###(<sha512>([\s\S]*?)</sha512>)###")};
std::smatch match_xml_version;
const bool has_xml_version = std::regex_search(XML.cbegin(), XML.cend(), match_xml_version, XML_VERSION_REGEX);
Checks::check_exit(VCPKG_LINE_INFO,
@ -98,43 +102,36 @@ namespace vcpkg::Commands::Fetch
XML_VERSION,
match_xml_version[1]);
const std::regex tool_regex{
Strings::format(R"###(<tool[\s]+name="%s"[\s]+os="%s">([\s\S]*?)<\/tool>)###", tool, OS_STRING)};
std::smatch match_tool;
const bool has_match_tool = std::regex_search(XML.cbegin(), XML.cend(), match_tool, tool_regex);
const std::regex tool_regex{Strings::format(R"###(<tool[\s]+name="%s"[\s]+os="%s">)###", tool, OS_STRING)};
std::smatch match_tool_entry;
const bool has_tool_entry = std::regex_search(XML.cbegin(), XML.cend(), match_tool_entry, tool_regex);
Checks::check_exit(VCPKG_LINE_INFO,
has_match_tool,
has_tool_entry,
"Could not find entry for tool [%s] in %s",
tool,
XML_PATH.generic_string());
const std::string tool_data_as_string = get_string_inside_tags(XML, tool_regex, tool);
const std::string required_version_as_string =
get_string_inside_tags(tool_data_as_string, VERSION_REGEX, "version");
const std::string url = get_string_inside_tags(tool_data_as_string, URL_REGEX, "url");
const std::string tool_data = get_string_inside_tags(XML, match_tool_entry[0], R"(</tool>)");
const std::string version_as_string = get_string_inside_tags(tool_data, "<version>", R"(</version>)");
const std::string exe_relative_path =
get_string_inside_tags(tool_data_as_string, EXE_RELATIVE_PATH_REGEX, "exeRelativePath");
get_string_inside_tags(tool_data, "<exeRelativePath>", R"(</exeRelativePath>)");
const std::string url = get_string_inside_tags(tool_data, "<url>", R"(</url>)");
const std::string sha512 = get_string_inside_tags(tool_data, "<sha512>", R"(</sha512>)");
auto archive_name = extract_string_between_delimiters(tool_data, "<archiveName>", R"(</archiveName>)");
auto archive_name = maybe_get_string_inside_tags(tool_data_as_string, ARCHIVE_NAME_REGEX);
const Optional<std::array<int, 3>> required_version = parse_version_string(required_version_as_string);
const Optional<std::array<int, 3>> version = parse_version_string(version_as_string);
Checks::check_exit(VCPKG_LINE_INFO,
required_version.has_value(),
version.has_value(),
"Could not parse version for tool %s. Version string was: %s",
tool,
required_version_as_string);
version_as_string);
const std::string tool_dir_name = Strings::format("%s-%s-%s", tool, required_version_as_string, OS_STRING);
const std::string tool_dir_name = Strings::format("%s-%s-%s", tool, version_as_string, OS_STRING);
const fs::path tool_dir_path = paths.downloads / "tools" / tool_dir_name;
const fs::path exe_path = tool_dir_path / exe_relative_path;
const std::string sha512 = get_string_inside_tags(tool_data_as_string, SHA512_REGEX, "sha512");
return ToolData{*required_version.get(),
return ToolData{*version.get(),
exe_path,
url,
paths.downloads / archive_name.value_or(exe_relative_path),