| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmExportPackageInfoGenerator.h"
- #include <memory>
- #include <set>
- #include <utility>
- #include <vector>
- #include <cm/string_view>
- #include <cmext/algorithm>
- #include <cmext/string_view>
- #include <cm3p/json/value.h>
- #include <cm3p/json/writer.h>
- #include "cmExportSet.h"
- #include "cmFindPackageStack.h"
- #include "cmGeneratorExpression.h"
- #include "cmGeneratorTarget.h"
- #include "cmList.h"
- #include "cmMakefile.h"
- #include "cmMessageType.h"
- #include "cmStringAlgorithms.h"
- #include "cmSystemTools.h"
- #include "cmTarget.h"
- #include "cmValue.h"
- static std::string const kCPS_VERSION_STR = "0.13.0";
- cmExportPackageInfoGenerator::cmExportPackageInfoGenerator(
- std::string packageName, std::string version, std::string versionCompat,
- std::string versionSchema, std::vector<std::string> defaultTargets,
- std::vector<std::string> defaultConfigurations)
- : PackageName(std::move(packageName))
- , PackageVersion(std::move(version))
- , PackageVersionCompat(std::move(versionCompat))
- , PackageVersionSchema(std::move(versionSchema))
- , DefaultTargets(std::move(defaultTargets))
- , DefaultConfigurations(std::move(defaultConfigurations))
- {
- }
- cm::string_view cmExportPackageInfoGenerator::GetImportPrefixWithSlash() const
- {
- return "@prefix@/"_s;
- }
- bool cmExportPackageInfoGenerator::GenerateImportFile(std::ostream& os)
- {
- return this->GenerateMainFile(os);
- }
- void cmExportPackageInfoGenerator::WritePackageInfo(
- Json::Value const& packageInfo, std::ostream& os) const
- {
- Json::StreamWriterBuilder builder;
- builder["indentation"] = " ";
- builder["commentStyle"] = "None";
- std::unique_ptr<Json::StreamWriter> const writer(builder.newStreamWriter());
- writer->write(packageInfo, &os);
- }
- namespace {
- template <typename T>
- void buildArray(Json::Value& object, std::string const& property,
- T const& values)
- {
- if (!values.empty()) {
- Json::Value& array = object[property];
- for (auto const& item : values) {
- array.append(item);
- }
- }
- }
- }
- bool cmExportPackageInfoGenerator::CheckDefaultTargets() const
- {
- bool result = true;
- std::set<std::string> exportedTargetNames;
- for (auto const* te : this->ExportedTargets) {
- exportedTargetNames.emplace(te->GetExportName());
- }
- for (auto const& name : this->DefaultTargets) {
- if (!cm::contains(exportedTargetNames, name)) {
- this->ReportError(
- cmStrCat("Package \"", this->GetPackageName(),
- "\" specifies DEFAULT_TARGETS \"", name,
- "\", which is not a target in the export set \"",
- this->GetExportSet()->GetName(), "\"."));
- result = false;
- }
- }
- return result;
- }
- Json::Value cmExportPackageInfoGenerator::GeneratePackageInfo() const
- {
- Json::Value package;
- package["name"] = this->GetPackageName();
- package["cps_version"] = std::string(kCPS_VERSION_STR);
- if (!this->PackageVersion.empty()) {
- package["version"] = this->PackageVersion;
- if (!this->PackageVersionCompat.empty()) {
- package["compat_version"] = this->PackageVersionCompat;
- }
- if (!this->PackageVersionSchema.empty()) {
- package["version_schema"] = this->PackageVersionSchema;
- }
- }
- buildArray(package, "default_components", this->DefaultTargets);
- buildArray(package, "configurations", this->DefaultConfigurations);
- // TODO: description, website, license
- return package;
- }
- void cmExportPackageInfoGenerator::GeneratePackageRequires(
- Json::Value& package) const
- {
- if (!this->Requirements.empty()) {
- Json::Value& requirements = package["requires"];
- // Build description for each requirement.
- for (auto const& requirement : this->Requirements) {
- auto data = Json::Value{ Json::objectValue };
- // Add required components.
- if (!requirement.second.empty()) {
- auto components = Json::Value{ Json::arrayValue };
- for (std::string const& component : requirement.second) {
- components.append(component);
- }
- data["components"] = components;
- }
- // TODO: version, hint
- requirements[requirement.first] = data;
- }
- }
- }
- Json::Value* cmExportPackageInfoGenerator::GenerateImportTarget(
- Json::Value& components, cmGeneratorTarget const* target,
- cmStateEnums::TargetType targetType) const
- {
- auto const& name = target->GetExportName();
- if (name.empty()) {
- return nullptr;
- }
- Json::Value& component = components[name];
- Json::Value& type = component["type"];
- switch (targetType) {
- case cmStateEnums::EXECUTABLE:
- type = "executable";
- break;
- case cmStateEnums::STATIC_LIBRARY:
- type = "archive";
- break;
- case cmStateEnums::SHARED_LIBRARY:
- type = "dylib";
- break;
- case cmStateEnums::MODULE_LIBRARY:
- type = "module";
- break;
- case cmStateEnums::INTERFACE_LIBRARY:
- type = "interface";
- break;
- default:
- type = "unknown";
- break;
- }
- return &component;
- }
- bool cmExportPackageInfoGenerator::GenerateInterfaceProperties(
- Json::Value& component, cmGeneratorTarget const* target,
- ImportPropertyMap const& properties) const
- {
- bool result = true;
- this->GenerateInterfaceLinkProperties(result, component, target, properties);
- this->GenerateInterfaceCompileFeatures(result, component, target,
- properties);
- this->GenerateInterfaceCompileDefines(result, component, target, properties);
- this->GenerateInterfaceListProperty(result, component, target,
- "compile_flags", "COMPILE_OPTIONS"_s,
- properties);
- this->GenerateInterfaceListProperty(result, component, target, "link_flags",
- "LINK_OPTIONS"_s, properties);
- this->GenerateInterfaceListProperty(result, component, target,
- "link_directories", "LINK_DIRECTORIES"_s,
- properties);
- this->GenerateInterfaceListProperty(result, component, target, "includes",
- "INCLUDE_DIRECTORIES"_s, properties);
- // TODO: description, license
- return result;
- }
- namespace {
- bool forbidGeneratorExpressions(std::string const& propertyName,
- std::string const& propertyValue,
- cmGeneratorTarget const* target)
- {
- std::string const& evaluatedValue = cmGeneratorExpression::Preprocess(
- propertyValue, cmGeneratorExpression::StripAllGeneratorExpressions);
- if (evaluatedValue != propertyValue) {
- target->Makefile->IssueMessage(
- MessageType::FATAL_ERROR,
- cmStrCat("Property \"", propertyName, "\" of target \"",
- target->GetName(),
- "\" contains a generator expression. This is not allowed."));
- return false;
- }
- return true;
- }
- }
- bool cmExportPackageInfoGenerator::NoteLinkedTarget(
- cmGeneratorTarget const* target, std::string const& linkedName,
- cmGeneratorTarget const* linkedTarget)
- {
- if (cm::contains(this->ExportedTargets, linkedTarget)) {
- // Target is internal to this package.
- this->LinkTargets.emplace(linkedName,
- cmStrCat(':', linkedTarget->GetExportName()));
- return true;
- }
- if (linkedTarget->IsImported()) {
- // Target is imported from a found package.
- auto pkgName = [linkedTarget]() -> std::string {
- auto const& pkgStack = linkedTarget->Target->GetFindPackageStack();
- if (!pkgStack.Empty()) {
- return pkgStack.Top().Name;
- }
- return linkedTarget->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME");
- }();
- if (pkgName.empty()) {
- target->Makefile->IssueMessage(
- MessageType::FATAL_ERROR,
- cmStrCat("Target \"", target->GetName(),
- "\" references imported target \"", linkedName,
- "\" which does not come from any known package."));
- return false;
- }
- auto const& prefix = cmStrCat(pkgName, "::");
- if (!cmHasPrefix(linkedName, prefix)) {
- target->Makefile->IssueMessage(
- MessageType::FATAL_ERROR,
- cmStrCat("Target \"", target->GetName(), "\" references target \"",
- linkedName, "\", which comes from the \"", pkgName,
- "\" package, but does not belong to the package's "
- "canonical namespace. This is not allowed."));
- return false;
- }
- std::string component = linkedName.substr(prefix.length());
- this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
- // TODO: Record package version, hint.
- this->Requirements[pkgName].emplace(std::move(component));
- return true;
- }
- // Target belongs to another export from this build.
- auto const& exportInfo = this->FindExportInfo(linkedTarget);
- if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
- auto const& linkNamespace = *exportInfo.Namespaces.begin();
- if (!cmHasSuffix(linkNamespace, "::")) {
- target->Makefile->IssueMessage(
- MessageType::FATAL_ERROR,
- cmStrCat("Target \"", target->GetName(), "\" references target \"",
- linkedName,
- "\", which does not use the standard namespace separator. "
- "This is not allowed."));
- return false;
- }
- std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 };
- std::string component = linkedTarget->GetExportName();
- if (pkgName == this->GetPackageName()) {
- this->LinkTargets.emplace(linkedName, cmStrCat(':', component));
- } else {
- this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
- this->Requirements[pkgName].emplace(std::move(component));
- }
- return true;
- }
- // Target belongs to multiple namespaces or multiple export sets.
- // cmExportFileGenerator::HandleMissingTarget should have complained about
- // this already.
- return false;
- }
- void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties(
- bool& result, Json::Value& component, cmGeneratorTarget const* target,
- ImportPropertyMap const& properties) const
- {
- auto const& iter = properties.find("INTERFACE_LINK_LIBRARIES");
- if (iter == properties.end()) {
- return;
- }
- // TODO: Support $<LINK_ONLY>.
- if (!forbidGeneratorExpressions(iter->first, iter->second, target)) {
- result = false;
- return;
- }
- std::vector<std::string> buildRequires;
- // std::vector<std::string> linkRequires; TODO
- std::vector<std::string> linkLibraries;
- for (auto const& name : cmList{ iter->second }) {
- auto const& ti = this->LinkTargets.find(name);
- if (ti != this->LinkTargets.end()) {
- if (ti->second.empty()) {
- result = false;
- } else {
- buildRequires.emplace_back(ti->second);
- }
- } else {
- linkLibraries.emplace_back(name);
- }
- }
- buildArray(component, "requires", buildRequires);
- // buildArray(component, "link_requires", linkRequires); TODO
- buildArray(component, "link_libraries", linkLibraries);
- }
- void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures(
- bool& result, Json::Value& component, cmGeneratorTarget const* target,
- ImportPropertyMap const& properties) const
- {
- auto const& iter = properties.find("INTERFACE_COMPILE_FEATURES");
- if (iter == properties.end()) {
- return;
- }
- if (!forbidGeneratorExpressions(iter->first, iter->second, target)) {
- result = false;
- return;
- }
- std::set<std::string> features;
- for (auto const& value : cmList{ iter->second }) {
- if (cmHasLiteralPrefix(value, "c_std_")) {
- auto suffix = cm::string_view{ value }.substr(6, 2);
- features.emplace(cmStrCat("cxx", suffix));
- } else if (cmHasLiteralPrefix(value, "cxx_std_")) {
- auto suffix = cm::string_view{ value }.substr(8, 2);
- features.emplace(cmStrCat("c++", suffix));
- }
- }
- buildArray(component, "compile_features", features);
- }
- void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines(
- bool& result, Json::Value& component, cmGeneratorTarget const* target,
- ImportPropertyMap const& properties) const
- {
- auto const& iter = properties.find("INTERFACE_COMPILE_DEFINITIONS");
- if (iter == properties.end()) {
- return;
- }
- // TODO: Support language-specific defines.
- if (!forbidGeneratorExpressions(iter->first, iter->second, target)) {
- result = false;
- return;
- }
- Json::Value defines;
- for (auto const& def : cmList{ iter->second }) {
- auto const n = def.find('=');
- if (n == std::string::npos) {
- defines[def] = Json::Value{};
- } else {
- defines[def.substr(0, n)] = def.substr(n + 1);
- }
- }
- if (!defines.empty()) {
- component["compile_definitions"]["*"] = std::move(defines);
- }
- }
- void cmExportPackageInfoGenerator::GenerateInterfaceListProperty(
- bool& result, Json::Value& component, cmGeneratorTarget const* target,
- std::string const& outName, cm::string_view inName,
- ImportPropertyMap const& properties) const
- {
- auto const& prop = cmStrCat("INTERFACE_", inName);
- auto const& iter = properties.find(prop);
- if (iter == properties.end()) {
- return;
- }
- if (!forbidGeneratorExpressions(prop, iter->second, target)) {
- result = false;
- return;
- }
- Json::Value& array = component[outName];
- for (auto const& value : cmList{ iter->second }) {
- array.append(value);
- }
- }
- Json::Value cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties(
- std::string const& suffix, ImportPropertyMap const& properties) const
- {
- Json::Value component;
- auto const suffixLength = suffix.length();
- for (auto const& p : properties) {
- if (!cmHasSuffix(p.first, suffix)) {
- continue;
- }
- auto const n = p.first.length() - suffixLength - 9;
- auto const prop = cm::string_view{ p.first }.substr(9, n);
- if (prop == "LOCATION") {
- component["location"] = p.second;
- } else if (prop == "IMPLIB") {
- component["link_location"] = p.second;
- } else if (prop == "LINK_INTERFACE_LANGUAGES") {
- std::vector<std::string> languages;
- for (auto const& lang : cmList{ p.second }) {
- auto ll = cmSystemTools::LowerCase(lang);
- if (ll == "cxx") {
- languages.emplace_back("cpp");
- } else {
- languages.emplace_back(std::move(ll));
- }
- }
- buildArray(component, "link_languages", languages);
- }
- }
- return component;
- }
|