mirror of
https://github.com/Kitware/CMake.git
synced 2025-05-10 07:10:32 +08:00

Tweak resolution of CPS prefix path to support the case of the .cps file being in the prefix root.
785 lines
24 KiB
C++
785 lines
24 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 <initializer_list>
|
|
#include <limits>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
#include <cmext/string_view>
|
|
|
|
#include <cm3p/json/reader.h>
|
|
#include <cm3p/json/value.h>
|
|
#include <cm3p/json/version.h>
|
|
|
|
#include "cmsys/FStream.hxx"
|
|
#include "cmsys/RegularExpression.hxx"
|
|
|
|
#include "cmExecutionStatus.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.h"
|
|
|
|
namespace {
|
|
|
|
// Map of CPS language names to CMake language name. Case insensitivity is
|
|
// achieved by converting the CPS value to lower case, so keys in this map must
|
|
// be lower case.
|
|
std::unordered_map<std::string, std::string> Languages = {
|
|
// clang-format off
|
|
{ "c", "C" },
|
|
{ "c++", "CXX" },
|
|
{ "cpp", "CXX" },
|
|
{ "cxx", "CXX" },
|
|
{ "objc", "OBJC" },
|
|
{ "objc++", "OBJCXX" },
|
|
{ "objcpp", "OBJCXX" },
|
|
{ "objcxx", "OBJCXX" },
|
|
{ "swift", "swift" },
|
|
{ "hip", "HIP" },
|
|
{ "cuda", "CUDA" },
|
|
{ "ispc", "ISPC" },
|
|
{ "c#", "CSharp" },
|
|
{ "csharp", "CSharp" },
|
|
{ "fortran", "Fortran" },
|
|
// clang-format on
|
|
};
|
|
|
|
enum LanguageGlobOption
|
|
{
|
|
DisallowGlob,
|
|
AllowGlob,
|
|
};
|
|
|
|
cm::string_view MapLanguage(cm::string_view lang,
|
|
LanguageGlobOption glob = AllowGlob)
|
|
{
|
|
if (glob == AllowGlob && lang == "*"_s) {
|
|
return "*"_s;
|
|
}
|
|
auto const li = Languages.find(cmSystemTools::LowerCase(lang));
|
|
if (li != Languages.end()) {
|
|
return li->second;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::string GetRealPath(std::string const& path)
|
|
{
|
|
return cmSystemTools::GetRealPath(path);
|
|
}
|
|
|
|
std::string GetRealDir(std::string const& path)
|
|
{
|
|
return cmSystemTools::GetFilenamePath(cmSystemTools::GetRealPath(path));
|
|
}
|
|
|
|
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.13") &&
|
|
cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, version, "1");
|
|
|
|
// TODO Eventually this probably needs to return the version tuple, and
|
|
// should share code with cmPackageInfoReader::ParseVersion.
|
|
}
|
|
|
|
bool ComparePathSuffix(std::string const& path, std::string const& suffix)
|
|
{
|
|
std::string const& tail = path.substr(path.size() - suffix.size());
|
|
return cmSystemTools::ComparePath(tail, suffix);
|
|
}
|
|
|
|
std::string DeterminePrefix(std::string const& filepath,
|
|
Json::Value const& data)
|
|
{
|
|
// First check if an absolute prefix was supplied.
|
|
std::string prefix = data["prefix"].asString();
|
|
if (!prefix.empty()) {
|
|
// Ensure that the specified prefix is valid.
|
|
if (cmsys::SystemTools::FileIsFullPath(prefix) &&
|
|
cmsys::SystemTools::FileIsDirectory(prefix)) {
|
|
cmSystemTools::ConvertToUnixSlashes(prefix);
|
|
return prefix;
|
|
}
|
|
// The specified absolute prefix is not valid.
|
|
return {};
|
|
}
|
|
|
|
// Get and validate prefix-relative path.
|
|
std::string const& absPath = cmSystemTools::GetFilenamePath(filepath);
|
|
std::string relPath = data["cps_path"].asString();
|
|
cmSystemTools::ConvertToUnixSlashes(relPath);
|
|
if (relPath.empty() || !cmHasLiteralPrefix(relPath, "@prefix@")) {
|
|
// The relative prefix is not valid.
|
|
return {};
|
|
}
|
|
if (relPath.size() == 8) {
|
|
// The relative path is exactly "@prefix@".
|
|
return absPath;
|
|
}
|
|
if (relPath[8] != '/') {
|
|
// The relative prefix is not valid.
|
|
return {};
|
|
}
|
|
relPath = relPath.substr(8);
|
|
|
|
// Get directory portion of the absolute path.
|
|
if (ComparePathSuffix(absPath, relPath)) {
|
|
return absPath.substr(0, absPath.size() - relPath.size());
|
|
}
|
|
|
|
for (auto* const f : { GetRealPath, GetRealDir }) {
|
|
std::string const& tmpPath = (*f)(absPath);
|
|
if (!cmSystemTools::ComparePath(tmpPath, absPath) &&
|
|
ComparePathSuffix(tmpPath, relPath)) {
|
|
return tmpPath.substr(0, tmpPath.size() - relPath.size());
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
// Extract key name from value iterator as string_view.
|
|
cm::string_view IterKey(Json::Value::const_iterator const& iter)
|
|
{
|
|
char const* end;
|
|
char const* const start = iter.memberName(&end);
|
|
return { start, static_cast<std::string::size_type>(end - start) };
|
|
}
|
|
|
|
// Get list-of-strings value from object.
|
|
std::vector<std::string> ReadList(Json::Value const& arr)
|
|
{
|
|
std::vector<std::string> result;
|
|
|
|
if (arr.isArray()) {
|
|
for (Json::Value const& val : arr) {
|
|
if (val.isString()) {
|
|
result.push_back(val.asString());
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::vector<std::string> ReadList(Json::Value const& data, char const* key)
|
|
{
|
|
return ReadList(data[key]);
|
|
}
|
|
|
|
std::string NormalizeTargetName(std::string const& name,
|
|
std::string const& context)
|
|
{
|
|
if (cmHasLiteralPrefix(name, ":")) {
|
|
return cmStrCat(context, name);
|
|
}
|
|
|
|
std::string::size_type const n = name.find_first_of(':');
|
|
if (n != std::string::npos) {
|
|
cm::string_view v{ name };
|
|
return cmStrCat(v.substr(0, n), ':', v.substr(n));
|
|
}
|
|
return name;
|
|
}
|
|
|
|
void AppendProperty(cmMakefile* makefile, cmTarget* target,
|
|
cm::string_view property, cm::string_view configuration,
|
|
std::string const& value)
|
|
{
|
|
std::string fullprop;
|
|
if (configuration.empty()) {
|
|
fullprop = cmStrCat("INTERFACE_"_s, property);
|
|
} else {
|
|
fullprop = cmStrCat("INTERFACE_"_s, property, '_',
|
|
cmSystemTools::UpperCase(configuration));
|
|
}
|
|
|
|
target->AppendProperty(fullprop, value, makefile->GetBacktrace());
|
|
}
|
|
|
|
template <typename Transform>
|
|
void AppendLanguageProperties(cmMakefile* makefile, cmTarget* target,
|
|
cm::string_view property,
|
|
cm::string_view configuration,
|
|
Json::Value const& data, char const* key,
|
|
Transform transform)
|
|
{
|
|
Json::Value const& value = data[key];
|
|
if (value.isArray()) {
|
|
for (std::string v : ReadList(value)) {
|
|
AppendProperty(makefile, target, property, configuration,
|
|
transform(std::move(v)));
|
|
}
|
|
} else if (value.isObject()) {
|
|
for (auto vi = value.begin(), ve = value.end(); vi != ve; ++vi) {
|
|
cm::string_view const originalLang = IterKey(vi);
|
|
cm::string_view const lang = MapLanguage(originalLang);
|
|
if (lang.empty()) {
|
|
makefile->IssueMessage(MessageType::WARNING,
|
|
cmStrCat(R"(ignoring unknown language ")"_s,
|
|
originalLang, R"(" in )"_s, key,
|
|
" for "_s, target->GetName()));
|
|
continue;
|
|
}
|
|
|
|
if (lang == "*"_s) {
|
|
for (std::string v : ReadList(*vi)) {
|
|
AppendProperty(makefile, target, property, configuration,
|
|
transform(std::move(v)));
|
|
}
|
|
} else {
|
|
for (std::string v : ReadList(*vi)) {
|
|
v = cmStrCat("$<$<COMPILE_LANGUAGE:"_s, lang, ">:"_s,
|
|
transform(std::move(v)), '>');
|
|
AppendProperty(makefile, target, property, configuration, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddCompileFeature(cmMakefile* makefile, cmTarget* target,
|
|
cm::string_view configuration, std::string const& value)
|
|
{
|
|
auto reLanguageLevel = []() -> cmsys::RegularExpression {
|
|
static cmsys::RegularExpression re{ "^[Cc]([+][+])?([0-9][0-9])$" };
|
|
return re;
|
|
}();
|
|
|
|
if (reLanguageLevel.find(value)) {
|
|
std::string::size_type const n = reLanguageLevel.end() - 2;
|
|
cm::string_view const featurePrefix = (n == 3 ? "cxx_std_"_s : "c_std_"_s);
|
|
if (configuration.empty()) {
|
|
AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {},
|
|
cmStrCat(featurePrefix, value.substr(n)));
|
|
} else {
|
|
std::string const& feature =
|
|
cmStrCat("$<$<CONFIG:"_s, configuration, ">:"_s, featurePrefix,
|
|
value.substr(n), '>');
|
|
AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {}, feature);
|
|
}
|
|
} else if (cmStrCaseEq(value, "gnu"_s)) {
|
|
// Not implemented in CMake at this time
|
|
} else if (cmStrCaseEq(value, "threads"_s)) {
|
|
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
|
|
"Threads::Threads");
|
|
}
|
|
}
|
|
|
|
void AddLinkFeature(cmMakefile* makefile, cmTarget* target,
|
|
cm::string_view configuration, std::string const& value)
|
|
{
|
|
if (cmStrCaseEq(value, "thread"_s)) {
|
|
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
|
|
"Threads::Threads");
|
|
}
|
|
}
|
|
|
|
std::string BuildDefinition(std::string const& name, Json::Value const& value)
|
|
{
|
|
if (!value.isNull() && value.isConvertibleTo(Json::stringValue)) {
|
|
return cmStrCat(name, '=', value.asString());
|
|
}
|
|
return name;
|
|
}
|
|
|
|
void AddDefinition(cmMakefile* makefile, cmTarget* target,
|
|
cm::string_view configuration,
|
|
std::string const& definition)
|
|
{
|
|
AppendProperty(makefile, target, "COMPILE_DEFINITIONS"_s, configuration,
|
|
definition);
|
|
}
|
|
|
|
using DefinitionLanguageMap = std::map<cm::string_view, Json::Value>;
|
|
using DefinitionsMap = std::map<std::string, DefinitionLanguageMap>;
|
|
|
|
void AddDefinitions(cmMakefile* makefile, cmTarget* target,
|
|
cm::string_view configuration,
|
|
DefinitionsMap const& definitions)
|
|
{
|
|
for (auto const& di : definitions) {
|
|
auto const& g = di.second.find("*"_s);
|
|
if (g != di.second.end()) {
|
|
std::string const& def = BuildDefinition(di.first, g->second);
|
|
if (di.second.size() == 1) {
|
|
// Only the non-language-specific definition exists.
|
|
AddDefinition(makefile, target, configuration, def);
|
|
continue;
|
|
}
|
|
|
|
// Create a genex to apply this definition to all languages except
|
|
// those that override it.
|
|
std::vector<cm::string_view> excludedLanguages;
|
|
for (auto const& li : di.second) {
|
|
if (li.first != "*"_s) {
|
|
excludedLanguages.emplace_back(li.first);
|
|
}
|
|
}
|
|
AddDefinition(makefile, target, configuration,
|
|
cmStrCat("$<$<NOT:$<COMPILE_LANGUAGE:"_s,
|
|
cmJoin(excludedLanguages, ","_s), ">>:"_s, def,
|
|
'>'));
|
|
}
|
|
|
|
// Add language-specific definitions.
|
|
for (auto const& li : di.second) {
|
|
if (li.first != "*"_s) {
|
|
AddDefinition(makefile, target, configuration,
|
|
cmStrCat("$<$<COMPILE_LANGUAGE:"_s, li.first, ">:"_s,
|
|
BuildDefinition(di.first, li.second), '>'));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cm::optional<cmPackageInfoReader::Pep440Version> ParseSimpleVersion(
|
|
std::string const& version)
|
|
{
|
|
if (version.empty()) {
|
|
return cm::nullopt;
|
|
}
|
|
|
|
cmPackageInfoReader::Pep440Version result;
|
|
result.Simple = true;
|
|
|
|
cm::string_view remnant{ version };
|
|
for (;;) {
|
|
// Find the next part separator.
|
|
std::string::size_type const n = remnant.find_first_of(".+-"_s);
|
|
if (n == 0) {
|
|
// The part is an empty string.
|
|
return cm::nullopt;
|
|
}
|
|
|
|
// Extract the part as a number.
|
|
cm::string_view const part = remnant.substr(0, n);
|
|
std::string::size_type const l = part.size();
|
|
std::string::size_type p;
|
|
unsigned long const value = std::stoul(std::string{ part }, &p);
|
|
if (p != l || value > std::numeric_limits<unsigned>::max()) {
|
|
// The part was not a valid number or is too big.
|
|
return cm::nullopt;
|
|
}
|
|
result.ReleaseComponents.push_back(static_cast<unsigned>(value));
|
|
|
|
// Have we consumed the entire input?
|
|
if (n == std::string::npos) {
|
|
return { std::move(result) };
|
|
}
|
|
|
|
// Lop off the current part.
|
|
char const sep = remnant[n];
|
|
remnant = remnant.substr(n + 1);
|
|
if (sep == '+' || sep == '-') {
|
|
// If we hit the local label, we're done.
|
|
result.LocalLabel = remnant;
|
|
return { std::move(result) };
|
|
}
|
|
|
|
// We just consumed a '.'; check that there's more.
|
|
if (remnant.empty()) {
|
|
// A trailing part separator is not allowed.
|
|
return cm::nullopt;
|
|
}
|
|
|
|
// Continue with the remaining input.
|
|
}
|
|
|
|
// Unreachable.
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read(
|
|
std::string const& path, cmPackageInfoReader const* parent)
|
|
{
|
|
// 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() || (!parent && !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;
|
|
}
|
|
|
|
std::string prefix = (parent ? parent->Prefix : DeterminePrefix(path, data));
|
|
if (prefix.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Seems sane enough to hand back to the caller.
|
|
std::unique_ptr<cmPackageInfoReader> reader{ new cmPackageInfoReader };
|
|
reader->Data = std::move(data);
|
|
reader->Prefix = std::move(prefix);
|
|
reader->Path = path;
|
|
|
|
// Determine other information we need to know immediately, or (if this is
|
|
// a supplemental reader) copy from the parent.
|
|
if (parent) {
|
|
reader->ComponentTargets = parent->ComponentTargets;
|
|
reader->DefaultConfigurations = parent->DefaultConfigurations;
|
|
} else {
|
|
reader->DefaultConfigurations = ReadList(reader->Data, "configurations");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
cm::optional<std::string> cmPackageInfoReader::GetCompatVersion() const
|
|
{
|
|
Json::Value const& version = this->Data["compat_version"];
|
|
if (version.isString()) {
|
|
return version.asString();
|
|
}
|
|
return cm::nullopt;
|
|
}
|
|
|
|
cm::optional<cmPackageInfoReader::Pep440Version>
|
|
cmPackageInfoReader::ParseVersion(
|
|
cm::optional<std::string> const& version) const
|
|
{
|
|
// Check that we have a version.
|
|
if (!version) {
|
|
return cm::nullopt;
|
|
}
|
|
|
|
// 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)) {
|
|
return ParseSimpleVersion(*version);
|
|
}
|
|
|
|
return cm::nullopt;
|
|
}
|
|
|
|
std::vector<cmPackageRequirement> cmPackageInfoReader::GetRequirements() const
|
|
{
|
|
std::vector<cmPackageRequirement> requirements;
|
|
|
|
auto const& requirementObjects = this->Data["requires"];
|
|
|
|
for (auto ri = requirementObjects.begin(), re = requirementObjects.end();
|
|
ri != re; ++ri) {
|
|
cmPackageRequirement r{ ri.name(), (*ri)["version"].asString(),
|
|
ReadList(*ri, "components"),
|
|
ReadList(*ri, "hints") };
|
|
requirements.emplace_back(std::move(r));
|
|
}
|
|
|
|
return requirements;
|
|
}
|
|
|
|
std::vector<std::string> cmPackageInfoReader::GetComponentNames() const
|
|
{
|
|
std::vector<std::string> componentNames;
|
|
|
|
Json::Value const& components = this->Data["components"];
|
|
for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
|
|
componentNames.emplace_back(ci.name());
|
|
}
|
|
|
|
return componentNames;
|
|
}
|
|
|
|
std::string cmPackageInfoReader::ResolvePath(std::string path) const
|
|
{
|
|
cmSystemTools::ConvertToUnixSlashes(path);
|
|
if (cmHasPrefix(path, "@prefix@"_s)) {
|
|
return cmStrCat(this->Prefix, path.substr(8));
|
|
}
|
|
if (!cmSystemTools::FileIsFullPath(path)) {
|
|
return cmStrCat(cmSystemTools::GetFilenamePath(this->Path), '/', path);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
void cmPackageInfoReader::SetOptionalProperty(cmTarget* target,
|
|
cm::string_view property,
|
|
cm::string_view configuration,
|
|
Json::Value const& value) const
|
|
{
|
|
if (!value.isNull()) {
|
|
std::string fullprop;
|
|
if (configuration.empty()) {
|
|
fullprop = cmStrCat("IMPORTED_"_s, property);
|
|
} else {
|
|
fullprop = cmStrCat("IMPORTED_"_s, property, '_',
|
|
cmSystemTools::UpperCase(configuration));
|
|
}
|
|
|
|
target->SetProperty(fullprop, this->ResolvePath(value.asString()));
|
|
}
|
|
}
|
|
|
|
void cmPackageInfoReader::SetTargetProperties(
|
|
cmMakefile* makefile, cmTarget* target, Json::Value const& data,
|
|
std::string const& package, cm::string_view configuration) const
|
|
{
|
|
// Add configuration (if applicable).
|
|
if (!configuration.empty()) {
|
|
target->AppendProperty("IMPORTED_CONFIGURATIONS",
|
|
cmSystemTools::UpperCase(configuration),
|
|
makefile->GetBacktrace());
|
|
}
|
|
|
|
// Add compile and link features.
|
|
for (std::string const& def : ReadList(data, "compile_features")) {
|
|
AddCompileFeature(makefile, target, configuration, def);
|
|
}
|
|
|
|
for (std::string const& def : ReadList(data, "link_features")) {
|
|
AddLinkFeature(makefile, target, configuration, def);
|
|
}
|
|
|
|
// Add compile definitions.
|
|
Json::Value const& defs = data["definitions"];
|
|
DefinitionsMap definitionsMap;
|
|
for (auto ldi = defs.begin(), lde = defs.end(); ldi != lde; ++ldi) {
|
|
cm::string_view const originalLang = IterKey(ldi);
|
|
cm::string_view const lang = MapLanguage(originalLang);
|
|
if (lang.empty()) {
|
|
makefile->IssueMessage(
|
|
MessageType::WARNING,
|
|
cmStrCat(R"(ignoring unknown language ")"_s, originalLang,
|
|
R"(" in definitions for )"_s, target->GetName()));
|
|
continue;
|
|
}
|
|
|
|
for (auto di = ldi->begin(), de = ldi->end(); di != de; ++di) {
|
|
definitionsMap[di.name()].emplace(lang, *di);
|
|
}
|
|
}
|
|
AddDefinitions(makefile, target, configuration, definitionsMap);
|
|
|
|
// Add include directories.
|
|
AppendLanguageProperties(makefile, target, "INCLUDE_DIRECTORIES"_s,
|
|
configuration, data, "includes",
|
|
[this](std::string p) -> std::string {
|
|
return this->ResolvePath(std::move(p));
|
|
});
|
|
|
|
// Add link name/location(s).
|
|
this->SetOptionalProperty(target, "LOCATION"_s, configuration,
|
|
data["location"]);
|
|
|
|
this->SetOptionalProperty(target, "IMPLIB"_s, configuration,
|
|
data["link_location"]);
|
|
|
|
this->SetOptionalProperty(target, "SONAME"_s, configuration,
|
|
data["link_name"]);
|
|
|
|
// Add link languages.
|
|
for (std::string const& originalLang : ReadList(data, "link_languages")) {
|
|
cm::string_view const lang = MapLanguage(originalLang, DisallowGlob);
|
|
if (!lang.empty()) {
|
|
AppendProperty(makefile, target, "LINK_LANGUAGES"_s, configuration,
|
|
std::string{ lang });
|
|
}
|
|
}
|
|
|
|
// Add transitive dependencies.
|
|
for (std::string const& dep : ReadList(data, "requires")) {
|
|
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
|
|
NormalizeTargetName(dep, package));
|
|
}
|
|
|
|
for (std::string const& dep : ReadList(data, "link_requires")) {
|
|
std::string const& lib =
|
|
cmStrCat("$<LINK_ONLY:"_s, NormalizeTargetName(dep, package), '>');
|
|
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
|
|
}
|
|
}
|
|
|
|
cmTarget* cmPackageInfoReader::AddLibraryComponent(
|
|
cmMakefile* makefile, cmStateEnums::TargetType type, std::string const& name,
|
|
Json::Value const& data, std::string const& package) const
|
|
{
|
|
// Create the imported target.
|
|
cmTarget* const target = makefile->AddImportedTarget(name, type, false);
|
|
|
|
// Set target properties.
|
|
this->SetTargetProperties(makefile, target, data, package, {});
|
|
auto const& cfgData = data["configurations"];
|
|
for (auto ci = cfgData.begin(), ce = cfgData.end(); ci != ce; ++ci) {
|
|
this->SetTargetProperties(makefile, target, *ci, package, IterKey(ci));
|
|
}
|
|
|
|
// Set default configurations.
|
|
if (!this->DefaultConfigurations.empty()) {
|
|
target->SetProperty("IMPORTED_CONFIGURATIONS",
|
|
cmJoin(this->DefaultConfigurations, ";"_s));
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
bool cmPackageInfoReader::ImportTargets(cmMakefile* makefile,
|
|
cmExecutionStatus& status)
|
|
{
|
|
std::string const& package = this->GetName();
|
|
|
|
// Read components.
|
|
Json::Value const& components = this->Data["components"];
|
|
|
|
for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
|
|
cm::string_view const& name = IterKey(ci);
|
|
std::string const& type =
|
|
cmSystemTools::LowerCase((*ci)["type"].asString());
|
|
|
|
// Get and validate full target name.
|
|
std::string const& fullName = cmStrCat(package, "::"_s, name);
|
|
{
|
|
std::string msg;
|
|
if (!makefile->EnforceUniqueName(fullName, msg)) {
|
|
status.SetError(msg);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
cmTarget* target = nullptr;
|
|
if (type == "symbolic"_s) {
|
|
// TODO
|
|
} else if (type == "dylib"_s) {
|
|
target = this->AddLibraryComponent(
|
|
makefile, cmStateEnums::SHARED_LIBRARY, fullName, *ci, package);
|
|
} else if (type == "module"_s) {
|
|
target = this->AddLibraryComponent(
|
|
makefile, cmStateEnums::MODULE_LIBRARY, fullName, *ci, package);
|
|
} else if (type == "archive"_s) {
|
|
target = this->AddLibraryComponent(
|
|
makefile, cmStateEnums::STATIC_LIBRARY, fullName, *ci, package);
|
|
} else if (type == "interface"_s) {
|
|
target = this->AddLibraryComponent(
|
|
makefile, cmStateEnums::INTERFACE_LIBRARY, fullName, *ci, package);
|
|
} else {
|
|
makefile->IssueMessage(MessageType::WARNING,
|
|
cmStrCat(R"(component ")"_s, fullName,
|
|
R"(" has unknown type ")"_s, type,
|
|
R"(" and was not imported)"_s));
|
|
}
|
|
|
|
if (target) {
|
|
this->ComponentTargets.emplace(std::string{ name }, target);
|
|
}
|
|
}
|
|
|
|
// Read default components.
|
|
std::vector<std::string> const& defaultComponents =
|
|
ReadList(this->Data, "default_components");
|
|
if (!defaultComponents.empty()) {
|
|
std::string msg;
|
|
if (!makefile->EnforceUniqueName(package, msg)) {
|
|
status.SetError(msg);
|
|
return false;
|
|
}
|
|
|
|
cmTarget* const target = makefile->AddImportedTarget(
|
|
package, cmStateEnums::INTERFACE_LIBRARY, false);
|
|
for (std::string const& name : defaultComponents) {
|
|
std::string const& fullName = cmStrCat(package, "::"_s, name);
|
|
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, {}, fullName);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool cmPackageInfoReader::ImportTargetConfigurations(
|
|
cmMakefile* makefile, cmExecutionStatus& status) const
|
|
{
|
|
std::string const& configuration = this->Data["configuration"].asString();
|
|
|
|
if (configuration.empty()) {
|
|
makefile->IssueMessage(MessageType::WARNING,
|
|
cmStrCat("supplemental file "_s, this->Path,
|
|
" does not specify a configuration"_s));
|
|
return true;
|
|
}
|
|
|
|
std::string const& package = this->GetName();
|
|
Json::Value const& components = this->Data["components"];
|
|
|
|
for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
|
|
// Get component name and look up target.
|
|
cm::string_view const& name = IterKey(ci);
|
|
auto const& ti = this->ComponentTargets.find(std::string{ name });
|
|
if (ti == this->ComponentTargets.end()) {
|
|
status.SetError(cmStrCat("component "_s, name, " was not found"_s));
|
|
return false;
|
|
}
|
|
|
|
// Read supplemental data for component.
|
|
this->SetTargetProperties(makefile, ti->second, *ci, package,
|
|
configuration);
|
|
}
|
|
|
|
return true;
|
|
}
|