You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
687 lines
25 KiB
687 lines
25 KiB
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmExportCMakeConfigGenerator.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <sstream>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cm/optional>
|
|
#include <cm/string_view>
|
|
#include <cmext/string_view>
|
|
|
|
#include "cmExportSet.h"
|
|
#include "cmFileSet.h"
|
|
#include "cmFindPackageStack.h"
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmLinkItem.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmPolicies.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.h"
|
|
#include "cmValue.h"
|
|
#include "cmVersion.h"
|
|
|
|
static std::string cmExportFileGeneratorEscape(std::string const& str)
|
|
{
|
|
// Escape a property value for writing into a .cmake file.
|
|
std::string result = cmOutputConverter::EscapeForCMake(str);
|
|
// Un-escape variable references generated by our own export code.
|
|
cmSystemTools::ReplaceString(result, "\\${_IMPORT_PREFIX}",
|
|
"${_IMPORT_PREFIX}");
|
|
cmSystemTools::ReplaceString(result, "\\${CMAKE_IMPORT_LIBRARY_SUFFIX}",
|
|
"${CMAKE_IMPORT_LIBRARY_SUFFIX}");
|
|
return result;
|
|
}
|
|
|
|
cmExportCMakeConfigGenerator::cmExportCMakeConfigGenerator() = default;
|
|
|
|
cm::string_view cmExportCMakeConfigGenerator::GetImportPrefixWithSlash() const
|
|
{
|
|
return "${_IMPORT_PREFIX}/"_s;
|
|
}
|
|
|
|
bool cmExportCMakeConfigGenerator::GenerateImportFile(std::ostream& os)
|
|
{
|
|
std::stringstream mainFileWithHeadersAndFootersBuffer;
|
|
|
|
// Start with the import file header.
|
|
this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer);
|
|
|
|
// Create all the imported targets.
|
|
std::stringstream mainFileBuffer;
|
|
bool result = this->GenerateMainFile(mainFileBuffer);
|
|
|
|
// Export find_dependency() calls. Must be done after GenerateMainFile(),
|
|
// because that's when target dependencies are gathered, which we need for
|
|
// the find_dependency() calls.
|
|
if (!this->AppendMode && this->GetExportSet() &&
|
|
this->ExportPackageDependencies) {
|
|
this->SetRequiredCMakeVersion(3, 9, 0);
|
|
this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer);
|
|
}
|
|
|
|
// Write cached import code.
|
|
mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf();
|
|
|
|
// End with the import file footer.
|
|
this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer);
|
|
this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer);
|
|
|
|
// This has to be done last, after the minimum CMake version has been
|
|
// determined.
|
|
this->GeneratePolicyHeaderCode(os);
|
|
os << mainFileWithHeadersAndFootersBuffer.rdbuf();
|
|
|
|
return result;
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateInterfaceProperties(
|
|
cmGeneratorTarget const* target, std::ostream& os,
|
|
ImportPropertyMap const& properties)
|
|
{
|
|
if (!properties.empty()) {
|
|
std::string targetName =
|
|
cmStrCat(this->Namespace, target->GetExportName());
|
|
os << "set_target_properties(" << targetName << " PROPERTIES\n";
|
|
for (auto const& property : properties) {
|
|
os << " " << property.first << " "
|
|
<< cmExportFileGeneratorEscape(property.second) << "\n";
|
|
}
|
|
os << ")\n\n";
|
|
}
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::SetImportLinkInterface(
|
|
std::string const& config, std::string const& suffix,
|
|
cmGeneratorExpression::PreprocessContext preprocessRule,
|
|
cmGeneratorTarget const* target, ImportPropertyMap& properties)
|
|
{
|
|
// Add the transitive link dependencies for this configuration.
|
|
cmLinkInterface const* iface = target->GetLinkInterface(config, target);
|
|
if (!iface) {
|
|
return;
|
|
}
|
|
|
|
if (iface->ImplementationIsInterface) {
|
|
// Policy CMP0022 must not be NEW.
|
|
this->SetImportLinkProperty(
|
|
suffix, target, "IMPORTED_LINK_INTERFACE_LIBRARIES", iface->Libraries,
|
|
properties, ImportLinkPropertyTargetNames::Yes);
|
|
return;
|
|
}
|
|
|
|
cmValue propContent;
|
|
|
|
if (cmValue prop_suffixed =
|
|
target->GetProperty("LINK_INTERFACE_LIBRARIES" + suffix)) {
|
|
propContent = prop_suffixed;
|
|
} else if (cmValue prop = target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
|
|
propContent = prop;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
bool const newCMP0022Behavior =
|
|
target->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
|
|
target->GetPolicyStatusCMP0022() != cmPolicies::OLD;
|
|
|
|
if (newCMP0022Behavior && !this->ExportOld) {
|
|
cmLocalGenerator* lg = target->GetLocalGenerator();
|
|
std::ostringstream e;
|
|
e << "Target \"" << target->GetName()
|
|
<< "\" has policy CMP0022 enabled, "
|
|
"but also has old-style LINK_INTERFACE_LIBRARIES properties "
|
|
"populated, but it was exported without the "
|
|
"EXPORT_LINK_INTERFACE_LIBRARIES to export the old-style properties";
|
|
lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
|
return;
|
|
}
|
|
|
|
if (propContent->empty()) {
|
|
properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix].clear();
|
|
return;
|
|
}
|
|
|
|
std::string prepro =
|
|
cmGeneratorExpression::Preprocess(*propContent, preprocessRule);
|
|
if (!prepro.empty()) {
|
|
this->ResolveTargetsInGeneratorExpressions(prepro, target,
|
|
ReplaceFreeTargets);
|
|
properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro;
|
|
}
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GeneratePolicyHeaderCode(std::ostream& os)
|
|
{
|
|
// Protect that file against use with older CMake versions.
|
|
/* clang-format off */
|
|
os << "# Generated by CMake\n\n";
|
|
os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n"
|
|
<< " message(FATAL_ERROR \"CMake >= "
|
|
<< this->RequiredCMakeVersionMajor << '.'
|
|
<< this->RequiredCMakeVersionMinor << '.'
|
|
<< this->RequiredCMakeVersionPatch << " required\")\n"
|
|
<< "endif()\n"
|
|
<< "if(CMAKE_VERSION VERSION_LESS \""
|
|
<< this->RequiredCMakeVersionMajor << '.'
|
|
<< this->RequiredCMakeVersionMinor << '.'
|
|
<< this->RequiredCMakeVersionPatch << "\")\n"
|
|
<< " message(FATAL_ERROR \"CMake >= "
|
|
<< this->RequiredCMakeVersionMajor << '.'
|
|
<< this->RequiredCMakeVersionMinor << '.'
|
|
<< this->RequiredCMakeVersionPatch << " required\")\n"
|
|
<< "endif()\n";
|
|
/* clang-format on */
|
|
|
|
// Isolate the file policy level.
|
|
// Support CMake versions as far back as the
|
|
// RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW
|
|
// policy settings for up to CMake 3.29 (this upper limit may be reviewed
|
|
// and increased from time to time). This reduces the opportunity for CMake
|
|
// warnings when an older export file is later used with newer CMake
|
|
// versions.
|
|
/* clang-format off */
|
|
os << "cmake_policy(PUSH)\n"
|
|
<< "cmake_policy(VERSION "
|
|
<< this->RequiredCMakeVersionMajor << '.'
|
|
<< this->RequiredCMakeVersionMinor << '.'
|
|
<< this->RequiredCMakeVersionPatch << "...3.29)\n";
|
|
/* clang-format on */
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GeneratePolicyFooterCode(std::ostream& os)
|
|
{
|
|
os << "cmake_policy(POP)\n";
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateImportHeaderCode(
|
|
std::ostream& os, std::string const& config)
|
|
{
|
|
os << "#----------------------------------------------------------------\n"
|
|
<< "# Generated CMake target import file";
|
|
if (!config.empty()) {
|
|
os << " for configuration \"" << config << "\".\n";
|
|
} else {
|
|
os << ".\n";
|
|
}
|
|
os << "#----------------------------------------------------------------\n"
|
|
<< "\n";
|
|
this->GenerateImportVersionCode(os);
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateImportFooterCode(std::ostream& os)
|
|
{
|
|
os << "# Commands beyond this point should not need to know the version.\n"
|
|
<< "set(CMAKE_IMPORT_FILE_VERSION)\n";
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateImportVersionCode(std::ostream& os)
|
|
{
|
|
// Store an import file format version. This will let us change the
|
|
// format later while still allowing old import files to work.
|
|
/* clang-format off */
|
|
os << "# Commands may need to know the format version.\n"
|
|
<< "set(CMAKE_IMPORT_FILE_VERSION 1)\n"
|
|
<< "\n";
|
|
/* clang-format on */
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateExpectedTargetsCode(
|
|
std::ostream& os, std::string const& expectedTargets)
|
|
{
|
|
/* clang-format off */
|
|
os << "# Protect against multiple inclusion, which would fail when already "
|
|
"imported targets are added once more.\n"
|
|
"set(_cmake_targets_defined \"\")\n"
|
|
"set(_cmake_targets_not_defined \"\")\n"
|
|
"set(_cmake_expected_targets \"\")\n"
|
|
"foreach(_cmake_expected_target IN ITEMS " << expectedTargets << ")\n"
|
|
" list(APPEND _cmake_expected_targets \"${_cmake_expected_target}\")\n"
|
|
" if(TARGET \"${_cmake_expected_target}\")\n"
|
|
" list(APPEND _cmake_targets_defined \"${_cmake_expected_target}\")\n"
|
|
" else()\n"
|
|
" list(APPEND _cmake_targets_not_defined \"${_cmake_expected_target}\")\n"
|
|
" endif()\n"
|
|
"endforeach()\n"
|
|
"unset(_cmake_expected_target)\n"
|
|
"if(_cmake_targets_defined STREQUAL _cmake_expected_targets)\n"
|
|
" unset(_cmake_targets_defined)\n"
|
|
" unset(_cmake_targets_not_defined)\n"
|
|
" unset(_cmake_expected_targets)\n"
|
|
" unset(CMAKE_IMPORT_FILE_VERSION)\n"
|
|
" cmake_policy(POP)\n"
|
|
" return()\n"
|
|
"endif()\n"
|
|
"if(NOT _cmake_targets_defined STREQUAL \"\")\n"
|
|
" string(REPLACE \";\" \", \" _cmake_targets_defined_text \"${_cmake_targets_defined}\")\n"
|
|
" string(REPLACE \";\" \", \" _cmake_targets_not_defined_text \"${_cmake_targets_not_defined}\")\n"
|
|
" message(FATAL_ERROR \"Some (but not all) targets in this export "
|
|
"set were already defined.\\nTargets Defined: ${_cmake_targets_defined_text}\\n"
|
|
"Targets not yet defined: ${_cmake_targets_not_defined_text}\\n\")\n"
|
|
"endif()\n"
|
|
"unset(_cmake_targets_defined)\n"
|
|
"unset(_cmake_targets_not_defined)\n"
|
|
"unset(_cmake_expected_targets)\n"
|
|
"\n\n";
|
|
/* clang-format on */
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateImportTargetCode(
|
|
std::ostream& os, cmGeneratorTarget const* target,
|
|
cmStateEnums::TargetType targetType)
|
|
{
|
|
// Construct the imported target name.
|
|
std::string targetName = this->Namespace;
|
|
|
|
targetName += target->GetExportName();
|
|
|
|
// Create the imported target.
|
|
os << "# Create imported target " << targetName << "\n";
|
|
switch (targetType) {
|
|
case cmStateEnums::EXECUTABLE:
|
|
os << "add_executable(" << targetName << " IMPORTED)\n";
|
|
break;
|
|
case cmStateEnums::STATIC_LIBRARY:
|
|
os << "add_library(" << targetName << " STATIC IMPORTED)\n";
|
|
break;
|
|
case cmStateEnums::SHARED_LIBRARY:
|
|
os << "add_library(" << targetName << " SHARED IMPORTED)\n";
|
|
break;
|
|
case cmStateEnums::MODULE_LIBRARY:
|
|
os << "add_library(" << targetName << " MODULE IMPORTED)\n";
|
|
break;
|
|
case cmStateEnums::UNKNOWN_LIBRARY:
|
|
os << "add_library(" << targetName << " UNKNOWN IMPORTED)\n";
|
|
break;
|
|
case cmStateEnums::OBJECT_LIBRARY:
|
|
os << "add_library(" << targetName << " OBJECT IMPORTED)\n";
|
|
break;
|
|
case cmStateEnums::INTERFACE_LIBRARY:
|
|
os << "add_library(" << targetName << " INTERFACE IMPORTED)\n";
|
|
break;
|
|
default: // should never happen
|
|
break;
|
|
}
|
|
|
|
// Mark the imported executable if it has exports.
|
|
if (target->IsExecutableWithExports() ||
|
|
(target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) {
|
|
os << "set_property(TARGET " << targetName
|
|
<< " PROPERTY ENABLE_EXPORTS 1)\n";
|
|
}
|
|
|
|
// Mark the imported library if it is a framework.
|
|
if (target->IsFrameworkOnApple()) {
|
|
os << "set_property(TARGET " << targetName << " PROPERTY FRAMEWORK 1)\n";
|
|
}
|
|
|
|
// Mark the imported executable if it is an application bundle.
|
|
if (target->IsAppBundleOnApple()) {
|
|
os << "set_property(TARGET " << targetName
|
|
<< " PROPERTY MACOSX_BUNDLE 1)\n";
|
|
}
|
|
|
|
if (target->IsCFBundleOnApple()) {
|
|
os << "set_property(TARGET " << targetName << " PROPERTY BUNDLE 1)\n";
|
|
}
|
|
|
|
// generate DEPRECATION
|
|
if (target->IsDeprecated()) {
|
|
os << "set_property(TARGET " << targetName << " PROPERTY DEPRECATION "
|
|
<< cmExportFileGeneratorEscape(target->GetDeprecation()) << ")\n";
|
|
}
|
|
|
|
if (target->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) {
|
|
os << "set_property(TARGET " << targetName
|
|
<< " PROPERTY IMPORTED_NO_SYSTEM 1)\n";
|
|
}
|
|
|
|
if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) {
|
|
os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n";
|
|
}
|
|
|
|
os << "\n";
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateImportPropertyCode(
|
|
std::ostream& os, std::string const& config, std::string const& suffix,
|
|
cmGeneratorTarget const* target, ImportPropertyMap const& properties,
|
|
std::string const& importedXcFrameworkLocation)
|
|
{
|
|
// Construct the imported target name.
|
|
std::string targetName = this->Namespace;
|
|
|
|
targetName += target->GetExportName();
|
|
|
|
// Set the import properties.
|
|
os << "# Import target \"" << targetName << "\" for configuration \""
|
|
<< config << "\"\n";
|
|
os << "set_property(TARGET " << targetName
|
|
<< " APPEND PROPERTY IMPORTED_CONFIGURATIONS ";
|
|
if (!config.empty()) {
|
|
os << cmSystemTools::UpperCase(config);
|
|
} else {
|
|
os << "NOCONFIG";
|
|
}
|
|
os << ")\n";
|
|
os << "set_target_properties(" << targetName << " PROPERTIES\n";
|
|
std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix);
|
|
for (auto const& property : properties) {
|
|
if (importedXcFrameworkLocation.empty() ||
|
|
property.first != importedLocationProp) {
|
|
os << " " << property.first << " "
|
|
<< cmExportFileGeneratorEscape(property.second) << "\n";
|
|
}
|
|
}
|
|
os << " )\n";
|
|
if (!importedXcFrameworkLocation.empty()) {
|
|
auto importedLocationIt = properties.find(importedLocationProp);
|
|
if (importedLocationIt != properties.end()) {
|
|
os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY "
|
|
<< cmExportFileGeneratorEscape(importedXcFrameworkLocation)
|
|
<< ")\n"
|
|
" set_property(TARGET "
|
|
<< targetName << " PROPERTY " << importedLocationProp << " "
|
|
<< cmExportFileGeneratorEscape(importedXcFrameworkLocation)
|
|
<< ")\nelse()\n set_property(TARGET " << targetName << " PROPERTY "
|
|
<< importedLocationProp << " "
|
|
<< cmExportFileGeneratorEscape(importedLocationIt->second)
|
|
<< ")\nendif()\n";
|
|
}
|
|
}
|
|
os << "\n";
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateFindDependencyCalls(
|
|
std::ostream& os)
|
|
{
|
|
os << "include(CMakeFindDependencyMacro)\n";
|
|
std::map<std::string, cmExportSet::PackageDependency> packageDependencies;
|
|
auto* exportSet = this->GetExportSet();
|
|
if (exportSet) {
|
|
packageDependencies = exportSet->GetPackageDependencies();
|
|
}
|
|
|
|
for (cmGeneratorTarget const* gt : this->ExternalTargets) {
|
|
std::string findPackageName;
|
|
auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME");
|
|
cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack();
|
|
if (!exportFindPackageName.IsEmpty()) {
|
|
findPackageName = *exportFindPackageName;
|
|
} else {
|
|
if (!pkgStack.Empty()) {
|
|
cmFindPackageCall const& fpc = pkgStack.Top();
|
|
findPackageName = fpc.Name;
|
|
}
|
|
}
|
|
if (!findPackageName.empty()) {
|
|
auto& dep = packageDependencies[findPackageName];
|
|
if (!pkgStack.Empty()) {
|
|
dep.FindPackageIndex = pkgStack.Top().Index;
|
|
}
|
|
if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) {
|
|
dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::pair<std::string, cmExportSet::PackageDependency>>
|
|
packageDependenciesSorted(packageDependencies.begin(),
|
|
packageDependencies.end());
|
|
std::sort(
|
|
packageDependenciesSorted.begin(), packageDependenciesSorted.end(),
|
|
[](std::pair<std::string, cmExportSet::PackageDependency> const& lhs,
|
|
std::pair<std::string, cmExportSet::PackageDependency> const& rhs)
|
|
-> bool {
|
|
if (lhs.second.SpecifiedIndex) {
|
|
if (rhs.second.SpecifiedIndex) {
|
|
return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex;
|
|
}
|
|
assert(rhs.second.FindPackageIndex);
|
|
return true;
|
|
}
|
|
assert(lhs.second.FindPackageIndex);
|
|
if (rhs.second.SpecifiedIndex) {
|
|
return false;
|
|
}
|
|
assert(rhs.second.FindPackageIndex);
|
|
return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex;
|
|
});
|
|
|
|
for (auto const& it : packageDependenciesSorted) {
|
|
if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) {
|
|
os << "find_dependency(" << it.first;
|
|
for (auto const& arg : it.second.ExtraArguments) {
|
|
os << " " << cmOutputConverter::EscapeForCMake(arg);
|
|
}
|
|
os << ")\n";
|
|
}
|
|
}
|
|
os << "\n\n";
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateMissingTargetsCheckCode(
|
|
std::ostream& os)
|
|
{
|
|
if (this->MissingTargets.empty()) {
|
|
/* clang-format off */
|
|
os << "# This file does not depend on other imported targets which have\n"
|
|
"# been exported from the same project but in a separate "
|
|
"export set.\n\n";
|
|
/* clang-format on */
|
|
return;
|
|
}
|
|
/* clang-format off */
|
|
os << "# Make sure the targets which have been exported in some other\n"
|
|
"# export set exist.\n"
|
|
"unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
|
|
"foreach(_target ";
|
|
/* clang-format on */
|
|
std::set<std::string> emitted;
|
|
for (std::string const& missingTarget : this->MissingTargets) {
|
|
if (emitted.insert(missingTarget).second) {
|
|
os << "\"" << missingTarget << "\" ";
|
|
}
|
|
}
|
|
/* clang-format off */
|
|
os << ")\n"
|
|
" if(NOT TARGET \"${_target}\" )\n"
|
|
" set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets \""
|
|
"${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets} ${_target}\")"
|
|
"\n"
|
|
" endif()\n"
|
|
"endforeach()\n"
|
|
"\n"
|
|
"if(DEFINED ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
|
|
" if(CMAKE_FIND_PACKAGE_NAME)\n"
|
|
" set( ${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)\n"
|
|
" set( ${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "
|
|
"\"The following imported targets are "
|
|
"referenced, but are missing: "
|
|
"${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n"
|
|
" else()\n"
|
|
" message(FATAL_ERROR \"The following imported targets are "
|
|
"referenced, but are missing: "
|
|
"${${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets}\")\n"
|
|
" endif()\n"
|
|
"endif()\n"
|
|
"unset(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE_targets)\n"
|
|
"\n";
|
|
/* clang-format on */
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateImportedFileCheckLoop(
|
|
std::ostream& os)
|
|
{
|
|
// Add code which verifies at cmake time that the file which is being
|
|
// imported actually exists on disk. This should in theory always be theory
|
|
// case, but still when packages are split into normal and development
|
|
// packages this might get broken (e.g. the Config.cmake could be part of
|
|
// the non-development package, something similar happened to me without
|
|
// on SUSE with a mysql pkg-config file, which claimed everything is fine,
|
|
// but the development package was not installed.).
|
|
/* clang-format off */
|
|
os << "# Loop over all imported files and verify that they actually exist\n"
|
|
"foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n"
|
|
" if(CMAKE_VERSION VERSION_LESS \"3.28\"\n"
|
|
" OR NOT DEFINED "
|
|
"_cmake_import_check_xcframework_for_${_cmake_target}\n"
|
|
" OR NOT IS_DIRECTORY "
|
|
"\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n"
|
|
" foreach(_cmake_file IN LISTS "
|
|
"\"_cmake_import_check_files_for_${_cmake_target}\")\n"
|
|
" if(NOT EXISTS \"${_cmake_file}\")\n"
|
|
" message(FATAL_ERROR \"The imported target "
|
|
"\\\"${_cmake_target}\\\" references the file\n"
|
|
" \\\"${_cmake_file}\\\"\n"
|
|
"but this file does not exist. Possible reasons include:\n"
|
|
"* The file was deleted, renamed, or moved to another location.\n"
|
|
"* An install or uninstall procedure did not complete successfully.\n"
|
|
"* The installation package was faulty and contained\n"
|
|
" \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n"
|
|
"but not all the files it references.\n"
|
|
"\")\n"
|
|
" endif()\n"
|
|
" endforeach()\n"
|
|
" endif()\n"
|
|
" unset(_cmake_file)\n"
|
|
" unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n"
|
|
"endforeach()\n"
|
|
"unset(_cmake_target)\n"
|
|
"unset(_cmake_import_check_targets)\n"
|
|
"\n";
|
|
/* clang-format on */
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateImportedFileChecksCode(
|
|
std::ostream& os, cmGeneratorTarget const* target,
|
|
ImportPropertyMap const& properties,
|
|
std::set<std::string> const& importedLocations,
|
|
std::string const& importedXcFrameworkLocation)
|
|
{
|
|
// Construct the imported target name.
|
|
std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
|
|
|
|
os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n";
|
|
if (!importedXcFrameworkLocation.empty()) {
|
|
os << "set(_cmake_import_check_xcframework_for_" << targetName << ' '
|
|
<< cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n";
|
|
}
|
|
os << "list(APPEND _cmake_import_check_files_for_" << targetName << " ";
|
|
|
|
for (std::string const& li : importedLocations) {
|
|
auto pi = properties.find(li);
|
|
if (pi != properties.end()) {
|
|
os << cmExportFileGeneratorEscape(pi->second) << " ";
|
|
}
|
|
}
|
|
|
|
os << ")\n\n";
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateTargetFileSets(
|
|
cmGeneratorTarget* gte, std::ostream& os, cmTargetExport const* te)
|
|
{
|
|
auto interfaceFileSets = gte->Target->GetAllInterfaceFileSets();
|
|
if (!interfaceFileSets.empty()) {
|
|
std::string targetName = cmStrCat(this->Namespace, gte->GetExportName());
|
|
os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.23.0\")\n"
|
|
" target_sources("
|
|
<< targetName << "\n";
|
|
|
|
for (auto const& name : interfaceFileSets) {
|
|
auto* fileSet = gte->Target->GetFileSet(name);
|
|
if (!fileSet) {
|
|
gte->Makefile->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
cmStrCat("File set \"", name,
|
|
"\" is listed in interface file sets of ", gte->GetName(),
|
|
" but has not been created"));
|
|
return;
|
|
}
|
|
|
|
os << " INTERFACE"
|
|
<< "\n FILE_SET " << cmOutputConverter::EscapeForCMake(name)
|
|
<< "\n TYPE "
|
|
<< cmOutputConverter::EscapeForCMake(fileSet->GetType())
|
|
<< "\n BASE_DIRS "
|
|
<< this->GetFileSetDirectories(gte, fileSet, te) << "\n FILES "
|
|
<< this->GetFileSetFiles(gte, fileSet, te) << "\n";
|
|
}
|
|
|
|
os << " )\nelse()\n set_property(TARGET " << targetName
|
|
<< "\n APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES";
|
|
for (auto const& name : interfaceFileSets) {
|
|
auto* fileSet = gte->Target->GetFileSet(name);
|
|
if (!fileSet) {
|
|
gte->Makefile->IssueMessage(
|
|
MessageType::FATAL_ERROR,
|
|
cmStrCat("File set \"", name,
|
|
"\" is listed in interface file sets of ", gte->GetName(),
|
|
" but has not been created"));
|
|
return;
|
|
}
|
|
|
|
if (fileSet->GetType() == "HEADERS"_s) {
|
|
os << "\n " << this->GetFileSetDirectories(gte, fileSet, te);
|
|
}
|
|
}
|
|
os << "\n )\nendif()\n\n";
|
|
}
|
|
}
|
|
|
|
std::string cmExportCMakeConfigGenerator::GetCxxModuleFile(
|
|
std::string const& name) const
|
|
{
|
|
auto const& cxxModuleDirname = this->GetCxxModulesDirectory();
|
|
if (cxxModuleDirname.empty()) {
|
|
return {};
|
|
}
|
|
|
|
return cmStrCat(cmSystemTools::GetFilenamePath(this->MainImportFile), '/',
|
|
cxxModuleDirname, "/cxx-modules-", name, ".cmake");
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::GenerateCxxModuleInformation(
|
|
std::string const& name, std::ostream& os)
|
|
{
|
|
auto const cxx_module_dirname = this->GetCxxModulesDirectory();
|
|
if (cxx_module_dirname.empty()) {
|
|
return;
|
|
}
|
|
|
|
// Write the include.
|
|
os << "# Include C++ module properties\n"
|
|
<< "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname
|
|
<< "/cxx-modules-" << name << ".cmake\")\n\n";
|
|
|
|
// Include all configuration-specific include files.
|
|
cmGeneratedFileStream ap(this->GetCxxModuleFile(name), true);
|
|
ap.SetCopyIfDifferent(true);
|
|
|
|
this->GenerateCxxModuleConfigInformation(name, ap);
|
|
}
|
|
|
|
void cmExportCMakeConfigGenerator::SetRequiredCMakeVersion(unsigned int major,
|
|
unsigned int minor,
|
|
unsigned int patch)
|
|
{
|
|
if (CMake_VERSION_ENCODE(major, minor, patch) >
|
|
CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor,
|
|
this->RequiredCMakeVersionMinor,
|
|
this->RequiredCMakeVersionPatch)) {
|
|
this->RequiredCMakeVersionMajor = major;
|
|
this->RequiredCMakeVersionMinor = minor;
|
|
this->RequiredCMakeVersionPatch = patch;
|
|
}
|
|
}
|