1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +08:00
Files
CMake/Source/cmPackageInfoReader.cxx
Matthew Woehlke 91c31ada23 find_package: Actually find .cps files
Add a helper class to read CPS files. Use this to teach find_package how
to consider and accept .cps files in its search. (Note that no version
testing is performed at this time.) Add a simple test that we can find a
package from a .cps file and correctly extract the version information.

Note that this doesn't actually import anything from CPS yet.
2024-12-13 08:58:24 -05:00

147 lines
3.9 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmPackageInfoReader.h"
#include <limits>
#include <cm/string_view>
#include <cmext/string_view>
#include <cm3p/json/reader.h>
#include <cm3p/json/value.h>
#include <cm3p/json/version.h>
#include "cmsys/FStream.hxx"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
namespace {
Json::Value ReadJson(std::string const& fileName)
{
// Open the specified file.
cmsys::ifstream file(fileName.c_str(), std::ios::in | std::ios::binary);
if (!file) {
#if JSONCPP_VERSION_HEXA < 0x01070300
return Json::Value::null;
#else
return Json::Value::nullSingleton();
#endif
}
// Read file content and translate JSON.
Json::Value data;
Json::CharReaderBuilder builder;
builder["collectComments"] = false;
if (!Json::parseFromStream(builder, file, &data, nullptr)) {
#if JSONCPP_VERSION_HEXA < 0x01070300
return Json::Value::null;
#else
return Json::Value::nullSingleton();
#endif
}
return data;
}
bool CheckSchemaVersion(Json::Value const& data)
{
std::string const& version = data["cps_version"].asString();
// Check that a valid version is specified.
if (version.empty()) {
return false;
}
// Check that we understand this version.
return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
version, "0.12") &&
cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, version, "1");
// TODO Eventually this probably needs to return the version tuple, and
// should share code with cmPackageInfoReader::ParseVersion.
}
} // namespace
std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read(
std::string const& path)
{
// Read file and perform some basic validation:
// - the input is valid JSON
// - the input is a JSON object
// - the input has a "cps_version" that we (in theory) know how to parse
Json::Value data = ReadJson(path);
if (!data.isObject() || !CheckSchemaVersion(data)) {
return nullptr;
}
// - the input has a "name" attribute that is a non-empty string
Json::Value const& name = data["name"];
if (!name.isString() || name.empty()) {
return nullptr;
}
// - the input has a "components" attribute that is a JSON object
if (!data["components"].isObject()) {
return nullptr;
}
// Seems sane enough to hand back to the caller.
std::unique_ptr<cmPackageInfoReader> reader{ new cmPackageInfoReader };
reader->Data = data;
return reader;
}
std::string cmPackageInfoReader::GetName() const
{
return this->Data["name"].asString();
}
cm::optional<std::string> cmPackageInfoReader::GetVersion() const
{
Json::Value const& version = this->Data["version"];
if (version.isString()) {
return version.asString();
}
return cm::nullopt;
}
std::vector<unsigned> cmPackageInfoReader::ParseVersion() const
{
// Check that we have a version.
cm::optional<std::string> const& version = this->GetVersion();
if (!version) {
return {};
}
std::vector<unsigned> result;
cm::string_view remnant{ *version };
// Check if we know how to parse the version.
Json::Value const& schema = this->Data["version_schema"];
if (schema.isNull() || cmStrCaseEq(schema.asString(), "simple"_s)) {
// Keep going until we run out of parts.
while (!remnant.empty()) {
std::string::size_type n = remnant.find('.');
cm::string_view part = remnant.substr(0, n);
if (n == std::string::npos) {
remnant = {};
} else {
remnant = remnant.substr(n + 1);
}
unsigned long const value = std::stoul(std::string{ part }, &n);
if (n == 0 || value > std::numeric_limits<unsigned>::max()) {
// The part was not a valid number or is too big.
return {};
}
result.push_back(static_cast<unsigned>(value));
}
}
return result;
}