diff options
30 files changed, 3161 insertions, 2517 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 1d2b7dd..f4ff102 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -199,14 +199,22 @@ add_library( cmEvaluatedTargetProperty.cxx cmEvaluatedTargetProperty.h cmExprParserHelper.cxx + cmExportAndroidMKGenerator.h + cmExportAndroidMKGenerator.cxx cmExportBuildAndroidMKGenerator.h cmExportBuildAndroidMKGenerator.cxx + cmExportBuildCMakeConfigGenerator.h + cmExportBuildCMakeConfigGenerator.cxx cmExportBuildFileGenerator.h cmExportBuildFileGenerator.cxx + cmExportCMakeConfigGenerator.h + cmExportCMakeConfigGenerator.cxx cmExportFileGenerator.h cmExportFileGenerator.cxx cmExportInstallAndroidMKGenerator.h cmExportInstallAndroidMKGenerator.cxx + cmExportInstallCMakeConfigGenerator.h + cmExportInstallCMakeConfigGenerator.cxx cmExportInstallFileGenerator.h cmExportInstallFileGenerator.cxx cmExportTryCompileFileGenerator.h @@ -311,6 +319,10 @@ add_library( cmGraphVizWriter.h cmImportedCxxModuleInfo.cxx cmImportedCxxModuleInfo.h + cmInstallAndroidMKExportGenerator.cxx + cmInstallAndroidMKExportGenerator.h + cmInstallCMakeConfigExportGenerator.cxx + cmInstallCMakeConfigExportGenerator.h cmInstallGenerator.h cmInstallGenerator.cxx cmInstallGetRuntimeDependenciesGenerator.h diff --git a/Source/cmExportAndroidMKGenerator.cxx b/Source/cmExportAndroidMKGenerator.cxx new file mode 100644 index 0000000..34dc1a7 --- /dev/null +++ b/Source/cmExportAndroidMKGenerator.cxx @@ -0,0 +1,165 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportAndroidMKGenerator.h" + +#include <sstream> +#include <utility> +#include <vector> + +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmGeneratorTarget.h" +#include "cmLinkItem.h" +#include "cmList.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" + +cmExportAndroidMKGenerator::cmExportAndroidMKGenerator() = default; + +cm::string_view cmExportAndroidMKGenerator::GetImportPrefixWithSlash() const +{ + return "$(_IMPORT_PREFIX)/"_s; +} + +bool cmExportAndroidMKGenerator::GenerateImportFile(std::ostream& os) +{ + if (!this->AppendMode) { + // Start with the import file header. + this->GenerateImportHeaderCode(os); + } + + // Create all the imported targets. + std::stringstream mainFileBuffer; + bool result = this->GenerateMainFile(mainFileBuffer); + + // Write cached import code. + os << mainFileBuffer.rdbuf(); + + return result; +} + +void cmExportAndroidMKGenerator::GenerateInterfaceProperties( + cmGeneratorTarget const* target, std::ostream& os, + ImportPropertyMap const& properties) +{ + std::string const config = + (this->Configurations.empty() ? std::string{} : this->Configurations[0]); + GenerateType const type = this->GetGenerateType(); + + bool const newCMP0022Behavior = + target->GetPolicyStatusCMP0022() != cmPolicies::WARN && + target->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (!newCMP0022Behavior) { + std::ostringstream w; + if (type == BUILD) { + w << "export(TARGETS ... ANDROID_MK) called with policy CMP0022"; + } else { + w << "install( EXPORT_ANDROID_MK ...) called with policy CMP0022"; + } + w << " set to OLD for target " << target->Target->GetName() << ". " + << "The export will only work with CMP0022 set to NEW."; + target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + } + if (!properties.empty()) { + os << "LOCAL_CPP_FEATURES := rtti exceptions\n"; + for (auto const& property : properties) { + if (property.first == "INTERFACE_COMPILE_OPTIONS") { + os << "LOCAL_CPP_FEATURES += "; + os << (property.second) << "\n"; + } else if (property.first == "INTERFACE_LINK_LIBRARIES") { + std::string staticLibs; + std::string sharedLibs; + std::string ldlibs; + cmLinkInterfaceLibraries const* linkIFace = + target->GetLinkInterfaceLibraries(config, target, + cmGeneratorTarget::UseTo::Link); + for (cmLinkItem const& item : linkIFace->Libraries) { + cmGeneratorTarget const* gt = item.Target; + std::string const& lib = item.AsStr(); + if (gt) { + + if (gt->GetType() == cmStateEnums::SHARED_LIBRARY || + gt->GetType() == cmStateEnums::MODULE_LIBRARY) { + sharedLibs += " " + lib; + } else { + staticLibs += " " + lib; + } + } else { + bool relpath = false; + if (type == INSTALL) { + relpath = cmHasLiteralPrefix(lib, "../"); + } + // check for full path or if it already has a -l, or + // in the case of an install check for relative paths + // if it is full or a link library then use string directly + if (cmSystemTools::FileIsFullPath(lib) || + cmHasLiteralPrefix(lib, "-l") || relpath) { + ldlibs += " " + lib; + // if it is not a path and does not have a -l then add -l + } else if (!lib.empty()) { + ldlibs += " -l" + lib; + } + } + } + if (!sharedLibs.empty()) { + os << "LOCAL_SHARED_LIBRARIES :=" << sharedLibs << "\n"; + } + if (!staticLibs.empty()) { + os << "LOCAL_STATIC_LIBRARIES :=" << staticLibs << "\n"; + } + if (!ldlibs.empty()) { + os << "LOCAL_EXPORT_LDLIBS :=" << ldlibs << "\n"; + } + } else if (property.first == "INTERFACE_INCLUDE_DIRECTORIES") { + std::string includes = property.second; + cmList includeList{ includes }; + os << "LOCAL_EXPORT_C_INCLUDES := "; + std::string end; + for (std::string const& i : includeList) { + os << end << i; + end = "\\\n"; + } + os << "\n"; + } else if (property.first == "INTERFACE_LINK_OPTIONS") { + os << "LOCAL_EXPORT_LDFLAGS := "; + cmList linkFlagsList{ property.second }; + os << linkFlagsList.join(" ") << "\n"; + } else { + os << "# " << property.first << " " << (property.second) << "\n"; + } + } + } + + // Tell the NDK build system if prebuilt static libraries use C++. + if (target->GetType() == cmStateEnums::STATIC_LIBRARY) { + cmLinkImplementation const* li = + target->GetLinkImplementation(config, cmGeneratorTarget::UseTo::Link); + if (cm::contains(li->Languages, "CXX")) { + os << "LOCAL_HAS_CPP := true\n"; + } + } + + switch (target->GetType()) { + case cmStateEnums::SHARED_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + os << "include $(PREBUILT_SHARED_LIBRARY)\n"; + break; + case cmStateEnums::STATIC_LIBRARY: + os << "include $(PREBUILT_STATIC_LIBRARY)\n"; + break; + case cmStateEnums::EXECUTABLE: + case cmStateEnums::UTILITY: + case cmStateEnums::OBJECT_LIBRARY: + case cmStateEnums::GLOBAL_TARGET: + case cmStateEnums::INTERFACE_LIBRARY: + case cmStateEnums::UNKNOWN_LIBRARY: + break; + } + os << "\n"; +} diff --git a/Source/cmExportAndroidMKGenerator.h b/Source/cmExportAndroidMKGenerator.h new file mode 100644 index 0000000..fb0f03b --- /dev/null +++ b/Source/cmExportAndroidMKGenerator.h @@ -0,0 +1,73 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <map> +#include <string> + +#include <cm/string_view> + +#include "cmExportFileGenerator.h" +#include "cmStateTypes.h" + +class cmGeneratorTarget; + +/** \class cmExportAndroidMKGenerator + * \brief Generate CMake configuration files exporting targets from a build or + * install tree. + * + * cmExportAndroidMKGenerator is the superclass for + * cmExportBuildAndroidMKGenerator and cmExportInstallAndroidMKGenerator. + * It contains common code generation routines for the two kinds of export + * implementations. + */ +class cmExportAndroidMKGenerator : virtual public cmExportFileGenerator +{ +public: + cmExportAndroidMKGenerator(); + + using cmExportFileGenerator::GenerateImportFile; + +protected: + enum GenerateType + { + BUILD, + INSTALL + }; + virtual GenerateType GetGenerateType() const = 0; + + using ImportPropertyMap = std::map<std::string, std::string>; + + cm::string_view GetImportPrefixWithSlash() const override; + + void GenerateInterfaceProperties(cmGeneratorTarget const* target, + std::ostream& os, + ImportPropertyMap const& properties); + + // Methods to implement export file code generation. + bool GenerateImportFile(std::ostream& os) override; + virtual void GenerateImportHeaderCode(std::ostream& os, + std::string const& config = "") = 0; + virtual void GenerateImportTargetCode( + std::ostream& os, cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType) = 0; + + void GenerateImportTargetsConfig(std::ostream& /*os*/, + std::string const& /*config*/, + std::string const& /*suffix*/) override + { + } + + std::string GetCxxModuleFile(std::string const& /*name*/) const override + { + return {}; + } + + void GenerateCxxModuleConfigInformation(std::string const& /*name*/, + std::ostream& /*os*/) const override + { + } +}; diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx index cbc05dd..a5f6ca3 100644 --- a/Source/cmExportBuildAndroidMKGenerator.cxx +++ b/Source/cmExportBuildAndroidMKGenerator.cxx @@ -2,43 +2,56 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportBuildAndroidMKGenerator.h" -#include <map> #include <sstream> -#include <utility> #include <vector> -#include <cmext/algorithm> - +#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" -#include "cmLinkItem.h" -#include "cmList.h" -#include "cmMakefile.h" -#include "cmMessageType.h" #include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" -cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator() -{ - this->LG = nullptr; - this->ExportSet = nullptr; -} +cmExportBuildAndroidMKGenerator::cmExportBuildAndroidMKGenerator() = default; -void cmExportBuildAndroidMKGenerator::GenerateImportHeaderCode( - std::ostream& os, const std::string&) +bool cmExportBuildAndroidMKGenerator::GenerateMainFile(std::ostream& os) { - os << "LOCAL_PATH := $(call my-dir)\n\n"; -} + if (!this->CollectExports([&](cmGeneratorTarget const*) {})) { + return false; + } -void cmExportBuildAndroidMKGenerator::GenerateImportFooterCode(std::ostream&) -{ + // Create all the imported targets. + for (auto const& exp : this->Exports) { + cmGeneratorTarget* gte = exp.Target; + + this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte)); + + gte->Target->AppendBuildInterfaceIncludes(); + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(gte, properties)) { + return false; + } + + bool const newCMP0022Behavior = + gte->GetPolicyStatusCMP0022() != cmPolicies::WARN && + gte->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) { + this->PopulateInterfaceLinkLibrariesProperty( + gte, cmGeneratorExpression::BuildInterface, properties); + } + + this->GenerateInterfaceProperties(gte, os, properties); + } + + return true; } -void cmExportBuildAndroidMKGenerator::GenerateExpectedTargetsCode( - std::ostream&, const std::string&) +void cmExportBuildAndroidMKGenerator::GenerateImportHeaderCode( + std::ostream& os, std::string const&) { + os << "LOCAL_PATH := $(call my-dir)\n\n"; } void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode( @@ -55,143 +68,3 @@ void cmExportBuildAndroidMKGenerator::GenerateImportTargetCode( cmSystemTools::ConvertToOutputPath(target->GetFullPath(noConfig)); os << path << "\n"; } - -void cmExportBuildAndroidMKGenerator::GenerateImportPropertyCode( - std::ostream&, const std::string&, const std::string&, - cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&) -{ -} - -void cmExportBuildAndroidMKGenerator::GenerateMissingTargetsCheckCode( - std::ostream&) -{ -} - -void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( - const cmGeneratorTarget* target, std::ostream& os, - const ImportPropertyMap& properties) -{ - std::string config; - if (!this->Configurations.empty()) { - config = this->Configurations[0]; - } - cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( - target, os, properties, cmExportBuildAndroidMKGenerator::BUILD, config); -} - -void cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( - const cmGeneratorTarget* target, std::ostream& os, - const ImportPropertyMap& properties, GenerateType type, - std::string const& config) -{ - const bool newCMP0022Behavior = - target->GetPolicyStatusCMP0022() != cmPolicies::WARN && - target->GetPolicyStatusCMP0022() != cmPolicies::OLD; - if (!newCMP0022Behavior) { - std::ostringstream w; - if (type == cmExportBuildAndroidMKGenerator::BUILD) { - w << "export(TARGETS ... ANDROID_MK) called with policy CMP0022"; - } else { - w << "install( EXPORT_ANDROID_MK ...) called with policy CMP0022"; - } - w << " set to OLD for target " << target->Target->GetName() << ". " - << "The export will only work with CMP0022 set to NEW."; - target->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); - } - if (!properties.empty()) { - os << "LOCAL_CPP_FEATURES := rtti exceptions\n"; - for (auto const& property : properties) { - if (property.first == "INTERFACE_COMPILE_OPTIONS") { - os << "LOCAL_CPP_FEATURES += "; - os << (property.second) << "\n"; - } else if (property.first == "INTERFACE_LINK_LIBRARIES") { - std::string staticLibs; - std::string sharedLibs; - std::string ldlibs; - cmLinkInterfaceLibraries const* linkIFace = - target->GetLinkInterfaceLibraries(config, target, - cmGeneratorTarget::UseTo::Link); - for (cmLinkItem const& item : linkIFace->Libraries) { - cmGeneratorTarget const* gt = item.Target; - std::string const& lib = item.AsStr(); - if (gt) { - - if (gt->GetType() == cmStateEnums::SHARED_LIBRARY || - gt->GetType() == cmStateEnums::MODULE_LIBRARY) { - sharedLibs += " " + lib; - } else { - staticLibs += " " + lib; - } - } else { - bool relpath = false; - if (type == cmExportBuildAndroidMKGenerator::INSTALL) { - relpath = cmHasLiteralPrefix(lib, "../"); - } - // check for full path or if it already has a -l, or - // in the case of an install check for relative paths - // if it is full or a link library then use string directly - if (cmSystemTools::FileIsFullPath(lib) || - cmHasLiteralPrefix(lib, "-l") || relpath) { - ldlibs += " " + lib; - // if it is not a path and does not have a -l then add -l - } else if (!lib.empty()) { - ldlibs += " -l" + lib; - } - } - } - if (!sharedLibs.empty()) { - os << "LOCAL_SHARED_LIBRARIES :=" << sharedLibs << "\n"; - } - if (!staticLibs.empty()) { - os << "LOCAL_STATIC_LIBRARIES :=" << staticLibs << "\n"; - } - if (!ldlibs.empty()) { - os << "LOCAL_EXPORT_LDLIBS :=" << ldlibs << "\n"; - } - } else if (property.first == "INTERFACE_INCLUDE_DIRECTORIES") { - std::string includes = property.second; - cmList includeList{ includes }; - os << "LOCAL_EXPORT_C_INCLUDES := "; - std::string end; - for (std::string const& i : includeList) { - os << end << i; - end = "\\\n"; - } - os << "\n"; - } else if (property.first == "INTERFACE_LINK_OPTIONS") { - os << "LOCAL_EXPORT_LDFLAGS := "; - cmList linkFlagsList{ property.second }; - os << linkFlagsList.join(" ") << "\n"; - } else { - os << "# " << property.first << " " << (property.second) << "\n"; - } - } - } - - // Tell the NDK build system if prebuilt static libraries use C++. - if (target->GetType() == cmStateEnums::STATIC_LIBRARY) { - cmLinkImplementation const* li = - target->GetLinkImplementation(config, cmGeneratorTarget::UseTo::Link); - if (cm::contains(li->Languages, "CXX")) { - os << "LOCAL_HAS_CPP := true\n"; - } - } - - switch (target->GetType()) { - case cmStateEnums::SHARED_LIBRARY: - case cmStateEnums::MODULE_LIBRARY: - os << "include $(PREBUILT_SHARED_LIBRARY)\n"; - break; - case cmStateEnums::STATIC_LIBRARY: - os << "include $(PREBUILT_STATIC_LIBRARY)\n"; - break; - case cmStateEnums::EXECUTABLE: - case cmStateEnums::UTILITY: - case cmStateEnums::OBJECT_LIBRARY: - case cmStateEnums::GLOBAL_TARGET: - case cmStateEnums::INTERFACE_LIBRARY: - case cmStateEnums::UNKNOWN_LIBRARY: - break; - } - os << "\n"; -} diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h index 9562cee..deb3893 100644 --- a/Source/cmExportBuildAndroidMKGenerator.h +++ b/Source/cmExportBuildAndroidMKGenerator.h @@ -7,6 +7,7 @@ #include <iosfwd> #include <string> +#include "cmExportAndroidMKGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmStateTypes.h" @@ -21,42 +22,26 @@ class cmGeneratorTarget; * * This is used to implement the export() command. */ -class cmExportBuildAndroidMKGenerator : public cmExportBuildFileGenerator +class cmExportBuildAndroidMKGenerator + : public cmExportBuildFileGenerator + , public cmExportAndroidMKGenerator { public: cmExportBuildAndroidMKGenerator(); - // this is so cmExportInstallAndroidMKGenerator can share this - // function as they are almost the same - enum GenerateType - { - BUILD, - INSTALL - }; - static void GenerateInterfaceProperties(cmGeneratorTarget const* target, - std::ostream& os, - const ImportPropertyMap& properties, - GenerateType type, - std::string const& config); + + /** Set whether to append generated code to the output file. */ + void SetAppendMode(bool append) { this->AppendMode = append; } protected: + GenerateType GetGenerateType() const override { return BUILD; } + // Implement virtual methods from the superclass. - void GeneratePolicyHeaderCode(std::ostream&) override {} - void GeneratePolicyFooterCode(std::ostream&) override {} + bool GenerateMainFile(std::ostream& os) override; void GenerateImportHeaderCode(std::ostream& os, - const std::string& config = "") override; - void GenerateImportFooterCode(std::ostream& os) override; + std::string const& config = "") override; void GenerateImportTargetCode( std::ostream& os, cmGeneratorTarget const* target, cmStateEnums::TargetType /*targetType*/) override; - void GenerateExpectedTargetsCode( - std::ostream& os, const std::string& expectedTargets) override; - void GenerateImportPropertyCode( - std::ostream& os, const std::string& config, const std::string& suffix, - cmGeneratorTarget const* target, ImportPropertyMap const& properties, - const std::string& importedXcFrameworkLocation) override; - void GenerateMissingTargetsCheckCode(std::ostream& os) override; - void GenerateFindDependencyCalls(std::ostream&) override {} - void GenerateInterfaceProperties( - cmGeneratorTarget const* target, std::ostream& os, - const ImportPropertyMap& properties) override; + + std::string GetCxxModulesDirectory() const override { return {}; } }; diff --git a/Source/cmExportBuildCMakeConfigGenerator.cxx b/Source/cmExportBuildCMakeConfigGenerator.cxx new file mode 100644 index 0000000..87aeb3c --- /dev/null +++ b/Source/cmExportBuildCMakeConfigGenerator.cxx @@ -0,0 +1,343 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportBuildCMakeConfigGenerator.h" + +#include <algorithm> +#include <cstddef> +#include <map> +#include <memory> +#include <set> +#include <sstream> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmCryptoHash.h" +#include "cmExportSet.h" +#include "cmFileSet.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.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" + +cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator() +{ + this->LG = nullptr; + this->ExportSet = nullptr; +} + +bool cmExportBuildCMakeConfigGenerator::GenerateMainFile(std::ostream& os) +{ + { + std::string expectedTargets; + std::string sep; + bool generatedInterfaceRequired = false; + auto visitor = [&](cmGeneratorTarget const* te) { + expectedTargets += sep + this->Namespace + te->GetExportName(); + sep = " "; + + generatedInterfaceRequired |= + this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY; + }; + + if (!this->CollectExports(visitor)) { + return false; + } + + if (generatedInterfaceRequired) { + this->SetRequiredCMakeVersion(3, 0, 0); + } + this->GenerateExpectedTargetsCode(os, expectedTargets); + } + + // Create all the imported targets. + for (auto const& exp : this->Exports) { + cmGeneratorTarget* gte = exp.Target; + this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte)); + + gte->Target->AppendBuildInterfaceIncludes(); + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(gte, properties)) { + return false; + } + + bool const newCMP0022Behavior = + gte->GetPolicyStatusCMP0022() != cmPolicies::WARN && + gte->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) { + this->PopulateInterfaceLinkLibrariesProperty( + gte, cmGeneratorExpression::BuildInterface, properties); + } + + this->GenerateInterfaceProperties(gte, os, properties); + + this->GenerateTargetFileSets(gte, os); + } + + std::string cxx_modules_name; + if (this->ExportSet) { + cxx_modules_name = this->ExportSet->GetName(); + } else { + cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512); + constexpr std::size_t HASH_TRUNCATION = 12; + for (auto const& target : this->Targets) { + hasher.Append(target.Name); + } + cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION); + } + + this->GenerateCxxModuleInformation(cxx_modules_name, os); + + // Generate import file content for each configuration. + for (std::string const& c : this->Configurations) { + this->GenerateImportConfig(os, c); + } + + // Generate import file content for each configuration. + for (std::string const& c : this->Configurations) { + this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, c); + } + + this->GenerateMissingTargetsCheckCode(os); + + return true; +} + +void cmExportBuildCMakeConfigGenerator::GenerateImportTargetsConfig( + std::ostream& os, std::string const& config, std::string const& suffix) +{ + for (auto const& exp : this->Exports) { + cmGeneratorTarget* target = exp.Target; + + // Collect import properties for this target. + ImportPropertyMap properties; + + if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) { + this->SetImportLocationProperty(config, suffix, target, properties); + } + if (!properties.empty()) { + // Get the rest of the target details. + if (this->GetExportTargetType(target) != + cmStateEnums::INTERFACE_LIBRARY) { + this->SetImportDetailProperties(config, suffix, target, properties); + this->SetImportLinkInterface(config, suffix, + cmGeneratorExpression::BuildInterface, + target, properties); + } + + // TODO: PUBLIC_HEADER_LOCATION + // This should wait until the build feature propagation stuff + // is done. Then this can be a propagated include directory. + // this->GenerateImportProperty(config, te->HeaderGenerator, + // properties); + + // Generate code in the export file. + std::string importedXcFrameworkLocation = exp.XcFrameworkLocation; + if (!importedXcFrameworkLocation.empty()) { + importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( + importedXcFrameworkLocation, + cmGeneratorExpression::PreprocessContext::BuildInterface); + importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( + importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config, + exp.Target, nullptr, exp.Target); + if (!importedXcFrameworkLocation.empty() && + !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) { + importedXcFrameworkLocation = + cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/', + importedXcFrameworkLocation); + } + } + this->GenerateImportPropertyCode(os, config, suffix, target, properties, + importedXcFrameworkLocation); + } + } +} + +namespace { +bool EntryIsContextSensitive( + std::unique_ptr<cmCompiledGeneratorExpression> const& cge) +{ + return cge->GetHadContextSensitiveCondition(); +} +} + +std::string cmExportBuildCMakeConfigGenerator::GetFileSetDirectories( + cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/) +{ + std::vector<std::string> resultVector; + + auto configs = + gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + auto directoryEntries = fileSet->CompileDirectoryEntries(); + + for (auto const& config : configs) { + auto directories = fileSet->EvaluateDirectoryEntries( + directoryEntries, gte->LocalGenerator, config, gte); + + bool const contextSensitive = + std::any_of(directoryEntries.begin(), directoryEntries.end(), + EntryIsContextSensitive); + + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && type == "CXX_MODULES"_s) { + auto* mf = this->LG->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base directory entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + + for (auto const& directory : directories) { + auto dest = cmOutputConverter::EscapeForCMake( + directory, cmOutputConverter::WrapQuotes::NoWrap); + + if (contextSensitive && configs.size() != 1) { + resultVector.push_back( + cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); + } else { + resultVector.emplace_back(cmStrCat('"', dest, '"')); + break; + } + } + } + + return cmJoin(resultVector, " "); +} + +std::string cmExportBuildCMakeConfigGenerator::GetFileSetFiles( + cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* /*te*/) +{ + std::vector<std::string> resultVector; + + auto configs = + gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + auto fileEntries = fileSet->CompileFileEntries(); + auto directoryEntries = fileSet->CompileDirectoryEntries(); + + for (auto const& config : configs) { + auto directories = fileSet->EvaluateDirectoryEntries( + directoryEntries, gte->LocalGenerator, config, gte); + + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + fileSet->EvaluateFileEntry(directories, files, entry, + gte->LocalGenerator, config, gte); + } + + bool const contextSensitive = + std::any_of(directoryEntries.begin(), directoryEntries.end(), + EntryIsContextSensitive) || + std::any_of(fileEntries.begin(), fileEntries.end(), + EntryIsContextSensitive); + + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && type == "CXX_MODULES"_s) { + auto* mf = this->LG->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + + for (auto const& it : files) { + for (auto const& filename : it.second) { + auto escapedFile = cmOutputConverter::EscapeForCMake( + filename, cmOutputConverter::WrapQuotes::NoWrap); + if (contextSensitive && configs.size() != 1) { + resultVector.push_back( + cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\"")); + } else { + resultVector.emplace_back(cmStrCat('"', escapedFile, '"')); + } + } + } + + if (!(contextSensitive && configs.size() != 1)) { + break; + } + } + + return cmJoin(resultVector, " "); +} + +void cmExportBuildCMakeConfigGenerator::GenerateCxxModuleConfigInformation( + std::string const& name, std::ostream& os) const +{ + char const* opt = ""; + if (this->Configurations.size() > 1) { + // With more than one configuration, each individual file is optional. + opt = " OPTIONAL"; + } + + // Generate import file content for each configuration. + for (std::string c : this->Configurations) { + if (c.empty()) { + c = "noconfig"; + } + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << '-' + << c << ".cmake\"" << opt << ")\n"; + } +} + +bool cmExportBuildCMakeConfigGenerator:: + GenerateImportCxxModuleConfigTargetInclusion(std::string const& name, + std::string config) const +{ + auto cxx_modules_dirname = this->GetCxxModulesDirectory(); + if (cxx_modules_dirname.empty()) { + return true; + } + + if (config.empty()) { + config = "noconfig"; + } + + std::string fileName = + cmStrCat(this->FileDir, '/', cxx_modules_dirname, "/cxx-modules-", name, + '-', config, ".cmake"); + + cmGeneratedFileStream os(fileName, true); + if (!os) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + os.SetCopyIfDifferent(true); + + for (auto const* tgt : this->ExportedTargets) { + // Only targets with C++ module sources will have a + // collator-generated install script. + if (!tgt->HaveCxx20ModuleSources()) { + continue; + } + + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-" + << tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n"; + } + + return true; +} diff --git a/Source/cmExportBuildCMakeConfigGenerator.h b/Source/cmExportBuildCMakeConfigGenerator.h new file mode 100644 index 0000000..0648dc3 --- /dev/null +++ b/Source/cmExportBuildCMakeConfigGenerator.h @@ -0,0 +1,52 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <string> + +#include "cmExportBuildFileGenerator.h" +#include "cmExportCMakeConfigGenerator.h" + +class cmFileSet; +class cmGeneratorTarget; +class cmTargetExport; + +/** \class cmExportBuildCMakeConfigGenerator + * \brief Generate a file exporting targets from a build tree. + * + * cmExportBuildCMakeConfigGenerator generates a file exporting targets from + * a build tree. This exports the targets to CMake's native package + * configuration format. A single file exports information for all + * configurations built. + * + * This is used to implement the export() command. + */ +class cmExportBuildCMakeConfigGenerator + : public cmExportCMakeConfigGenerator + , public cmExportBuildFileGenerator +{ +public: + cmExportBuildCMakeConfigGenerator(); + + /** Set whether to append generated code to the output file. */ + void SetAppendMode(bool append) { this->AppendMode = append; } + +protected: + // Implement virtual methods from the superclass. + bool GenerateMainFile(std::ostream& os) override; + void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, + std::string const& suffix) override; + + std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, + cmTargetExport const* te) override; + std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, + cmTargetExport const* te) override; + + void GenerateCxxModuleConfigInformation(std::string const&, + std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&, + std::string) const; +}; diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index d877d76..eefc516 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -3,20 +3,13 @@ #include "cmExportBuildFileGenerator.h" #include <algorithm> -#include <cstddef> #include <map> #include <memory> #include <set> #include <sstream> #include <utility> -#include <cm/string_view> -#include <cmext/string_view> - -#include "cmCryptoHash.h" #include "cmExportSet.h" -#include "cmFileSet.h" -#include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -24,11 +17,8 @@ #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 "cmTargetExport.h" #include "cmValue.h" @@ -50,195 +40,6 @@ void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg) } } -bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) -{ - { - std::string expectedTargets; - std::string sep; - std::vector<TargetExport> targets; - bool generatedInterfaceRequired = false; - this->GetTargets(targets); - for (auto const& tei : targets) { - cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name); - expectedTargets += sep + this->Namespace + te->GetExportName(); - sep = " "; - if (this->ExportedTargets.insert(te).second) { - this->Exports.emplace_back(te, tei.XcFrameworkLocation); - } else { - std::ostringstream e; - e << "given target \"" << te->GetName() << "\" more than once."; - this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), - this->LG->GetMakefile()->GetBacktrace()); - return false; - } - generatedInterfaceRequired |= - this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY; - } - - if (generatedInterfaceRequired) { - this->SetRequiredCMakeVersion(3, 0, 0); - } - this->GenerateExpectedTargetsCode(os, expectedTargets); - } - - // Create all the imported targets. - for (auto const& exp : this->Exports) { - cmGeneratorTarget* gte = exp.Target; - this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte)); - - gte->Target->AppendBuildInterfaceIncludes(); - - ImportPropertyMap properties; - - this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte, - cmGeneratorExpression::BuildInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte, - properties); - - std::string errorMessage; - if (!this->PopulateCxxModuleExportProperties( - gte, properties, cmGeneratorExpression::BuildInterface, {}, - errorMessage)) { - this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, errorMessage, - this->LG->GetMakefile()->GetBacktrace()); - return false; - } - - if (!this->PopulateExportProperties(gte, properties, errorMessage)) { - this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, errorMessage, - this->LG->GetMakefile()->GetBacktrace()); - return false; - } - - const bool newCMP0022Behavior = - gte->GetPolicyStatusCMP0022() != cmPolicies::WARN && - gte->GetPolicyStatusCMP0022() != cmPolicies::OLD; - if (newCMP0022Behavior) { - this->PopulateInterfaceLinkLibrariesProperty( - gte, cmGeneratorExpression::BuildInterface, properties); - } - this->PopulateCompatibleInterfaceProperties(gte, properties); - this->PopulateCustomTransitiveInterfaceProperties( - gte, cmGeneratorExpression::BuildInterface, properties); - - this->GenerateInterfaceProperties(gte, os, properties); - - this->GenerateTargetFileSets(gte, os); - } - - std::string cxx_modules_name; - if (this->ExportSet) { - cxx_modules_name = this->ExportSet->GetName(); - } else { - cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512); - constexpr std::size_t HASH_TRUNCATION = 12; - for (auto const& target : this->Targets) { - hasher.Append(target.Name); - } - cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION); - } - - this->GenerateCxxModuleInformation(cxx_modules_name, os); - - // Generate import file content for each configuration. - for (std::string const& c : this->Configurations) { - this->GenerateImportConfig(os, c); - } - - // Generate import file content for each configuration. - for (std::string const& c : this->Configurations) { - this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, c); - } - - this->GenerateMissingTargetsCheckCode(os); - - return true; -} - -void cmExportBuildFileGenerator::GenerateImportTargetsConfig( - std::ostream& os, const std::string& config, std::string const& suffix) -{ - for (auto const& exp : this->Exports) { - cmGeneratorTarget* target = exp.Target; - - // Collect import properties for this target. - ImportPropertyMap properties; - - if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) { - this->SetImportLocationProperty(config, suffix, target, properties); - } - if (!properties.empty()) { - // Get the rest of the target details. - if (this->GetExportTargetType(target) != - cmStateEnums::INTERFACE_LIBRARY) { - this->SetImportDetailProperties(config, suffix, target, properties); - this->SetImportLinkInterface(config, suffix, - cmGeneratorExpression::BuildInterface, - target, properties); - } - - // TODO: PUBLIC_HEADER_LOCATION - // This should wait until the build feature propagation stuff - // is done. Then this can be a propagated include directory. - // this->GenerateImportProperty(config, te->HeaderGenerator, - // properties); - - // Generate code in the export file. - std::string importedXcFrameworkLocation = exp.XcFrameworkLocation; - if (!importedXcFrameworkLocation.empty()) { - importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( - importedXcFrameworkLocation, - cmGeneratorExpression::PreprocessContext::BuildInterface); - importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( - importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config, - exp.Target, nullptr, exp.Target); - if (!importedXcFrameworkLocation.empty() && - !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) { - importedXcFrameworkLocation = - cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/', - importedXcFrameworkLocation); - } - } - this->GenerateImportPropertyCode(os, config, suffix, target, properties, - importedXcFrameworkLocation); - } - } -} - cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType( cmGeneratorTarget const* target) const { @@ -260,7 +61,7 @@ void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet) } void cmExportBuildFileGenerator::SetImportLocationProperty( - const std::string& config, std::string const& suffix, + std::string const& config, std::string const& suffix, cmGeneratorTarget* target, ImportPropertyMap& properties) { // Get the makefile in which to lookup target information. @@ -276,7 +77,7 @@ void cmExportBuildFileGenerator::SetImportLocationProperty( std::string const obj_dir = target->GetObjectDirectory(config); std::vector<std::string> objects; for (cmSourceFile const* sf : objectSources) { - const std::string& obj = target->GetObjectName(sf); + std::string const& obj = target->GetObjectName(sf); objects.push_back(obj_dir + obj); } @@ -311,13 +112,33 @@ void cmExportBuildFileGenerator::SetImportLocationProperty( } } +bool cmExportBuildFileGenerator::CollectExports( + std::function<void(cmGeneratorTarget const*)> visitor) +{ + auto pred = [&](cmExportBuildFileGenerator::TargetExport& tei) -> bool { + cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name); + if (this->ExportedTargets.insert(te).second) { + this->Exports.emplace_back(te, tei.XcFrameworkLocation); + visitor(te); + return true; + } + + this->ComplainAboutDuplicateTarget(te->GetName()); + return false; + }; + + std::vector<TargetExport> targets; + this->GetTargets(targets); + return std::all_of(targets.begin(), targets.end(), pred); +} + void cmExportBuildFileGenerator::HandleMissingTarget( std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) { // The target is not in the export. if (!this->AppendMode) { - const std::string name = dependee->GetName(); + std::string const& name = dependee->GetName(); cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator(); auto exportInfo = this->FindBuildExportInfo(gg, name); @@ -359,7 +180,7 @@ void cmExportBuildFileGenerator::GetTargets( std::pair<std::vector<std::string>, std::string> cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg, - const std::string& name) + std::string const& name) { std::vector<std::string> exportFiles; std::string ns; @@ -367,12 +188,12 @@ cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg, auto& exportSets = gg->GetBuildExportSets(); for (auto const& exp : exportSets) { - const auto& exportSet = exp.second; + auto const& exportSet = exp.second; std::vector<TargetExport> targets; exportSet->GetTargets(targets); if (std::any_of( targets.begin(), targets.end(), - [&name](const TargetExport& te) { return te.Name == name; })) { + [&name](TargetExport const& te) { return te.Name == name; })) { exportFiles.push_back(exp.first); ns = exportSet->GetNamespace(); } @@ -383,7 +204,7 @@ cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg, void cmExportBuildFileGenerator::ComplainAboutMissingTarget( cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, - std::vector<std::string> const& exportFiles) + std::vector<std::string> const& exportFiles) const { std::ostringstream e; e << "export called with target \"" << depender->GetName() @@ -399,13 +220,27 @@ void cmExportBuildFileGenerator::ComplainAboutMissingTarget( << dependee->GetName() << "\" target to a single export."; } + this->ReportError(e.str()); +} + +void cmExportBuildFileGenerator::ComplainAboutDuplicateTarget( + std::string const& targetName) const +{ + std::ostringstream e; + e << "given target \"" << targetName << "\" more than once."; + this->ReportError(e.str()); +} + +void cmExportBuildFileGenerator::ReportError( + std::string const& errorMessage) const +{ this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e.str(), + MessageType::FATAL_ERROR, errorMessage, this->LG->GetMakefile()->GetBacktrace()); } std::string cmExportBuildFileGenerator::InstallNameDir( - cmGeneratorTarget const* target, const std::string& config) + cmGeneratorTarget const* target, std::string const& config) { std::string install_name_dir; @@ -417,185 +252,22 @@ std::string cmExportBuildFileGenerator::InstallNameDir( return install_name_dir; } -namespace { -bool EntryIsContextSensitive( - const std::unique_ptr<cmCompiledGeneratorExpression>& cge) -{ - return cge->GetHadContextSensitiveCondition(); -} -} - -std::string cmExportBuildFileGenerator::GetFileSetDirectories( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* /*te*/) -{ - std::vector<std::string> resultVector; - - auto configs = - gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - auto directoryEntries = fileSet->CompileDirectoryEntries(); - - for (auto const& config : configs) { - auto directories = fileSet->EvaluateDirectoryEntries( - directoryEntries, gte->LocalGenerator, config, gte); - - bool const contextSensitive = - std::any_of(directoryEntries.begin(), directoryEntries.end(), - EntryIsContextSensitive); - - auto const& type = fileSet->GetType(); - // C++ modules do not support interface file sets which are dependent upon - // the configuration. - if (contextSensitive && type == "CXX_MODULES"_s) { - auto* mf = this->LG->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive base directory entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return std::string{}; - } - - for (auto const& directory : directories) { - auto dest = cmOutputConverter::EscapeForCMake( - directory, cmOutputConverter::WrapQuotes::NoWrap); - - if (contextSensitive && configs.size() != 1) { - resultVector.push_back( - cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); - } else { - resultVector.emplace_back(cmStrCat('"', dest, '"')); - break; - } - } - } - - return cmJoin(resultVector, " "); -} - -std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte, - cmFileSet* fileSet, - cmTargetExport* /*te*/) +bool cmExportBuildFileGenerator::PopulateInterfaceProperties( + cmGeneratorTarget const* target, ImportPropertyMap& properties) { - std::vector<std::string> resultVector; - - auto configs = - gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - - auto fileEntries = fileSet->CompileFileEntries(); - auto directoryEntries = fileSet->CompileDirectoryEntries(); - - for (auto const& config : configs) { - auto directories = fileSet->EvaluateDirectoryEntries( - directoryEntries, gte->LocalGenerator, config, gte); - - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - fileSet->EvaluateFileEntry(directories, files, entry, - gte->LocalGenerator, config, gte); - } - - bool const contextSensitive = - std::any_of(directoryEntries.begin(), directoryEntries.end(), - EntryIsContextSensitive) || - std::any_of(fileEntries.begin(), fileEntries.end(), - EntryIsContextSensitive); - - auto const& type = fileSet->GetType(); - // C++ modules do not support interface file sets which are dependent upon - // the configuration. - if (contextSensitive && type == "CXX_MODULES"_s) { - auto* mf = this->LG->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive file entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return std::string{}; - } - - for (auto const& it : files) { - for (auto const& filename : it.second) { - auto escapedFile = cmOutputConverter::EscapeForCMake( - filename, cmOutputConverter::WrapQuotes::NoWrap); - if (contextSensitive && configs.size() != 1) { - resultVector.push_back( - cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\"")); - } else { - resultVector.emplace_back(cmStrCat('"', escapedFile, '"')); - } - } - } - - if (!(contextSensitive && configs.size() != 1)) { - break; - } - } - - return cmJoin(resultVector, " "); -} - -std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const -{ - return this->CxxModulesDirectory; -} - -void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation( - std::string const& name, std::ostream& os) const -{ - const char* opt = ""; - if (this->Configurations.size() > 1) { - // With more than one configuration, each individual file is optional. - opt = " OPTIONAL"; - } - - // Generate import file content for each configuration. - for (std::string c : this->Configurations) { - if (c.empty()) { - c = "noconfig"; - } - os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << '-' - << c << ".cmake\"" << opt << ")\n"; - } -} - -bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion( - std::string const& name, std::string config) const -{ - auto cxx_modules_dirname = this->GetCxxModulesDirectory(); - if (cxx_modules_dirname.empty()) { - return true; - } - - if (config.empty()) { - config = "noconfig"; - } - - std::string fileName = - cmStrCat(this->FileDir, '/', cxx_modules_dirname, "/cxx-modules-", name, - '-', config, ".cmake"); - - cmGeneratedFileStream os(fileName, true); - if (!os) { - std::string se = cmSystemTools::GetLastSystemError(); - std::ostringstream e; - e << "cannot write to file \"" << fileName << "\": " << se; - cmSystemTools::Error(e.str()); - return false; - } - os.SetCopyIfDifferent(true); - - for (auto const* tgt : this->ExportedTargets) { - // Only targets with C++ module sources will have a - // collator-generated install script. - if (!tgt->HaveCxx20ModuleSources()) { - continue; - } - - os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-" - << tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n"; - } - - return true; + this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", target, + cmGeneratorExpression::BuildInterface, + properties); + this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", target, + cmGeneratorExpression::BuildInterface, + properties); + this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", target, + cmGeneratorExpression::BuildInterface, + properties); + this->PopulateInterfaceProperty("INTERFACE_SOURCES", target, + cmGeneratorExpression::BuildInterface, + properties); + + return this->PopulateInterfaceProperties( + target, {}, cmGeneratorExpression::BuildInterface, properties); } diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index ee4779f..a4f8c5f 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -4,7 +4,7 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include <iosfwd> +#include <functional> #include <string> #include <utility> #include <vector> @@ -15,22 +15,19 @@ #include "cmStateTypes.h" class cmExportSet; -class cmFileSet; class cmGeneratorTarget; class cmGlobalGenerator; class cmLocalGenerator; -class cmTargetExport; -/** \class cmExportBuildFileGenerator +/** \class cmExportBuildCMakeConfigGenerator * \brief Generate a file exporting targets from a build tree. * - * cmExportBuildFileGenerator generates a file exporting targets from - * a build tree. A single file exports information for all - * configurations built. + * cmExportBuildCMakeConfigGenerator is the interface class for generating a + * file exporting targets from a build tree. * * This is used to implement the export() command. */ -class cmExportBuildFileGenerator : public cmExportFileGenerator +class cmExportBuildFileGenerator : virtual public cmExportFileGenerator { public: struct TargetExport @@ -64,54 +61,56 @@ public: { this->CxxModulesDirectory = std::move(cxx_module_dir); } - const std::string& GetCxxModuleDirectory() const + std::string const& GetCxxModuleDirectory() const { return this->CxxModulesDirectory; } - /** Set whether to append generated code to the output file. */ - void SetAppendMode(bool append) { this->AppendMode = append; } - void Compute(cmLocalGenerator* lg); protected: - // Implement virtual methods from the superclass. - bool GenerateMainFile(std::ostream& os) override; - void GenerateImportTargetsConfig(std::ostream& os, const std::string& config, - std::string const& suffix) override; cmStateEnums::TargetType GetExportTargetType( cmGeneratorTarget const* target) const; + + /** Walk the list of targets to be exported. Returns true iff no duplicates + are found. */ + bool CollectExports(std::function<void(cmGeneratorTarget const*)> visitor); + void HandleMissingTarget(std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) override; - void ComplainAboutMissingTarget(cmGeneratorTarget const* depender, - cmGeneratorTarget const* dependee, - std::vector<std::string> const& namespaces); + void ComplainAboutMissingTarget( + cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, + std::vector<std::string> const& namespaces) const; + + void ComplainAboutDuplicateTarget( + std::string const& targetName) const override; + + void ReportError(std::string const& errorMessage) const override; /** Fill in properties indicating built file locations. */ - void SetImportLocationProperty(const std::string& config, + void SetImportLocationProperty(std::string const& config, std::string const& suffix, cmGeneratorTarget* target, ImportPropertyMap& properties); std::string InstallNameDir(cmGeneratorTarget const* target, - const std::string& config) override; + std::string const& config) override; - std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, - cmTargetExport* te) override; - std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, - cmTargetExport* te) override; cmExportSet* GetExportSet() const override { return this->ExportSet; } - std::string GetCxxModulesDirectory() const override; - void GenerateCxxModuleConfigInformation(std::string const&, - std::ostream&) const override; - bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&, - std::string) const; + std::string GetCxxModulesDirectory() const override + { + return this->CxxModulesDirectory; + } std::pair<std::vector<std::string>, std::string> FindBuildExportInfo( - cmGlobalGenerator* gg, const std::string& name); + cmGlobalGenerator* gg, std::string const& name); + + using cmExportFileGenerator::PopulateInterfaceProperties; + bool PopulateInterfaceProperties(cmGeneratorTarget const* target, + ImportPropertyMap& properties); struct TargetExportPrivate { diff --git a/Source/cmExportCMakeConfigGenerator.cxx b/Source/cmExportCMakeConfigGenerator.cxx new file mode 100644 index 0000000..4f4765c --- /dev/null +++ b/Source/cmExportCMakeConfigGenerator.cxx @@ -0,0 +1,686 @@ +/* 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; + } +} diff --git a/Source/cmExportCMakeConfigGenerator.h b/Source/cmExportCMakeConfigGenerator.h new file mode 100644 index 0000000..90f7aa7 --- /dev/null +++ b/Source/cmExportCMakeConfigGenerator.h @@ -0,0 +1,109 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <map> +#include <set> +#include <string> + +#include <cm/string_view> + +#include "cmExportFileGenerator.h" +#include "cmGeneratorExpression.h" +#include "cmStateTypes.h" + +class cmFileSet; +class cmGeneratorTarget; +class cmTargetExport; + +/** \class cmExportCMakeConfigGenerator + * \brief Generate CMake configuration files exporting targets from a build or + * install tree. + * + * cmExportCMakeConfigGenerator is the superclass for + * cmExportBuildCMakeConfigGenerator and cmExportInstallCMakeConfigGenerator. + * It contains common code generation routines for the two kinds of export + * implementations. + */ +class cmExportCMakeConfigGenerator : virtual public cmExportFileGenerator +{ +public: + cmExportCMakeConfigGenerator(); + + void SetExportOld(bool exportOld) { this->ExportOld = exportOld; } + + void SetExportPackageDependencies(bool exportPackageDependencies) + { + this->ExportPackageDependencies = exportPackageDependencies; + } + + using cmExportFileGenerator::GenerateImportFile; + +protected: + using ImportPropertyMap = std::map<std::string, std::string>; + + // Methods to implement export file code generation. + bool GenerateImportFile(std::ostream& os) override; + virtual void GeneratePolicyHeaderCode(std::ostream& os); + virtual void GeneratePolicyFooterCode(std::ostream& os); + virtual void GenerateImportHeaderCode(std::ostream& os, + std::string const& config = ""); + virtual void GenerateImportFooterCode(std::ostream& os); + void GenerateImportVersionCode(std::ostream& os); + virtual void GenerateImportTargetCode(std::ostream& os, + cmGeneratorTarget const* target, + cmStateEnums::TargetType targetType); + virtual void GenerateImportPropertyCode( + std::ostream& os, std::string const& config, std::string const& suffix, + cmGeneratorTarget const* target, ImportPropertyMap const& properties, + std::string const& importedXcFrameworkLocation); + virtual void GenerateImportedFileChecksCode( + std::ostream& os, cmGeneratorTarget const* target, + ImportPropertyMap const& properties, + std::set<std::string> const& importedLocations, + std::string const& importedXcFrameworkLocation); + virtual void GenerateImportedFileCheckLoop(std::ostream& os); + virtual void GenerateMissingTargetsCheckCode(std::ostream& os); + virtual void GenerateFindDependencyCalls(std::ostream& os); + + virtual void GenerateExpectedTargetsCode(std::ostream& os, + std::string const& expectedTargets); + + cm::string_view GetImportPrefixWithSlash() const override; + + virtual void GenerateInterfaceProperties( + cmGeneratorTarget const* target, std::ostream& os, + ImportPropertyMap const& properties); + + void SetImportLinkInterface( + std::string const& config, std::string const& suffix, + cmGeneratorExpression::PreprocessContext preprocessRule, + cmGeneratorTarget const* target, ImportPropertyMap& properties); + + void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, + cmTargetExport const* te = nullptr); + + std::string GetCxxModuleFile(std::string const& name) const override; + + void GenerateCxxModuleInformation(std::string const& name, std::ostream& os); + + virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, + cmFileSet* fileSet, + cmTargetExport const* te) = 0; + virtual std::string GetFileSetFiles(cmGeneratorTarget* gte, + cmFileSet* fileSet, + cmTargetExport const* te) = 0; + + void SetRequiredCMakeVersion(unsigned int major, unsigned int minor, + unsigned int patch); + + bool ExportOld = false; + bool ExportPackageDependencies = false; + + unsigned int RequiredCMakeVersionMajor = 2; + unsigned int RequiredCMakeVersionMinor = 8; + unsigned int RequiredCMakeVersionPatch = 3; +}; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 0cb0011..00f3c74 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -19,6 +19,7 @@ #include "cmExecutionStatus.h" #include "cmExperimental.h" #include "cmExportBuildAndroidMKGenerator.h" +#include "cmExportBuildCMakeConfigGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSet.h" #include "cmGeneratedFileStream.h" @@ -318,21 +319,24 @@ bool cmExportCommand(std::vector<std::string> const& args, // Setup export file generation. std::unique_ptr<cmExportBuildFileGenerator> ebfg = nullptr; if (android) { - ebfg = cm::make_unique<cmExportBuildAndroidMKGenerator>(); + auto ebag = cm::make_unique<cmExportBuildAndroidMKGenerator>(); + ebag->SetAppendMode(arguments.Append); + ebfg = std::move(ebag); } else { - ebfg = cm::make_unique<cmExportBuildFileGenerator>(); + auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>(); + ebcg->SetAppendMode(arguments.Append); + ebcg->SetExportOld(arguments.ExportOld); + ebcg->SetExportPackageDependencies(arguments.ExportPackageDependencies); + ebfg = std::move(ebcg); } ebfg->SetExportFile(fname.c_str()); ebfg->SetNamespace(arguments.Namespace); ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory); - ebfg->SetAppendMode(arguments.Append); if (exportSet != nullptr) { ebfg->SetExportSet(exportSet); } else { ebfg->SetTargets(targets); } - ebfg->SetExportOld(arguments.ExportOld); - ebfg->SetExportPackageDependencies(arguments.ExportPackageDependencies); // Compute the set of configurations exported. std::vector<std::string> configurationTypes = diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 4dddefa..24ed273 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -2,23 +2,18 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportFileGenerator.h" -#include <algorithm> #include <array> -#include <cassert> -#include <cstring> +#include <cstddef> #include <sstream> #include <utility> #include <cm/memory> -#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmsys/FStream.hxx" #include "cmComputeLinkInformation.h" -#include "cmExportSet.h" -#include "cmFileSet.h" #include "cmFindPackageStack.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" @@ -27,40 +22,21 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" -#include "cmPolicies.h" #include "cmPropertyMap.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; -} - -cmExportFileGenerator::cmExportFileGenerator() -{ - this->AppendMode = false; - this->ExportOld = false; -} +cmExportFileGenerator::cmExportFileGenerator() = default; -void cmExportFileGenerator::AddConfiguration(const std::string& config) +void cmExportFileGenerator::AddConfiguration(std::string const& config) { this->Configurations.push_back(config); } -void cmExportFileGenerator::SetExportFile(const char* mainFile) +void cmExportFileGenerator::SetExportFile(char const* mainFile) { this->MainImportFile = mainFile; this->FileDir = cmSystemTools::GetFilenamePath(this->MainImportFile); @@ -70,7 +46,7 @@ void cmExportFileGenerator::SetExportFile(const char* mainFile) cmSystemTools::GetFilenameLastExtension(this->MainImportFile); } -const std::string& cmExportFileGenerator::GetMainExportFileName() const +std::string const& cmExportFileGenerator::GetMainExportFileName() const { return this->MainImportFile; } @@ -98,42 +74,12 @@ bool cmExportFileGenerator::GenerateImportFile() cmSystemTools::Error(e.str()); return false; } - std::ostream& os = *foutPtr; - 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; + return this->GenerateImportFile(*foutPtr); } void cmExportFileGenerator::GenerateImportConfig(std::ostream& os, - const std::string& config) + std::string const& config) { // Construct the property configuration suffix. std::string suffix = "_"; @@ -147,10 +93,51 @@ void cmExportFileGenerator::GenerateImportConfig(std::ostream& os, this->GenerateImportTargetsConfig(os, config, suffix); } -void cmExportFileGenerator::PopulateInterfaceProperty( - const std::string& propName, cmGeneratorTarget const* target, +bool cmExportFileGenerator::PopulateInterfaceProperties( + cmGeneratorTarget const* target, std::string const& includesDestinationDirs, + cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties) { + this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", target, + preprocessRule, properties); + this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", + target, properties); + + std::string errorMessage; + if (!this->PopulateCxxModuleExportProperties( + target, properties, preprocessRule, includesDestinationDirs, + errorMessage)) { + this->ReportError(errorMessage); + return false; + } + + if (!this->PopulateExportProperties(target, properties, errorMessage)) { + this->ReportError(errorMessage); + return false; + } + this->PopulateCompatibleInterfaceProperties(target, properties); + this->PopulateCustomTransitiveInterfaceProperties(target, preprocessRule, + properties); + + return true; +} + +void cmExportFileGenerator::PopulateInterfaceProperty( + std::string const& propName, cmGeneratorTarget const* target, + ImportPropertyMap& properties) const +{ cmValue input = target->GetProperty(propName); if (input) { properties[propName] = *input; @@ -158,7 +145,7 @@ void cmExportFileGenerator::PopulateInterfaceProperty( } void cmExportFileGenerator::PopulateInterfaceProperty( - const std::string& propName, const std::string& outputName, + std::string const& propName, std::string const& outputName, cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties) @@ -180,6 +167,15 @@ void cmExportFileGenerator::PopulateInterfaceProperty( } } +void cmExportFileGenerator::PopulateInterfaceProperty( + std::string const& propName, cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + this->PopulateInterfaceProperty(propName, propName, target, preprocessRule, + properties); +} + bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext preprocessRule, @@ -188,7 +184,7 @@ bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( if (!target->IsLinkable()) { return false; } - static const std::array<std::string, 3> linkIfaceProps = { + static std::array<std::string, 3> const linkIfaceProps = { { "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT", "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" } }; @@ -208,319 +204,27 @@ bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty( return hadINTERFACE_LINK_LIBRARIES; } -static bool isSubDirectory(std::string const& a, std::string const& b) -{ - return (cmSystemTools::ComparePath(a, b) || - cmSystemTools::IsSubDirectory(a, b)); -} - -static bool checkInterfaceDirs(const std::string& prepro, - cmGeneratorTarget const* target, - const std::string& prop) -{ - std::string const& installDir = - target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); - std::string const& topSourceDir = - target->GetLocalGenerator()->GetSourceDirectory(); - std::string const& topBinaryDir = - target->GetLocalGenerator()->GetBinaryDirectory(); - - std::vector<std::string> parts; - cmGeneratorExpression::Split(prepro, parts); - - const bool inSourceBuild = topSourceDir == topBinaryDir; - - bool hadFatalError = false; - - for (std::string const& li : parts) { - size_t genexPos = cmGeneratorExpression::Find(li); - if (genexPos == 0) { - continue; - } - if (cmHasLiteralPrefix(li, "${_IMPORT_PREFIX}")) { - continue; - } - MessageType messageType = MessageType::FATAL_ERROR; - std::ostringstream e; - if (genexPos != std::string::npos) { - if (prop == "INTERFACE_INCLUDE_DIRECTORIES") { - switch (target->GetPolicyStatusCMP0041()) { - case cmPolicies::WARN: - messageType = MessageType::WARNING; - e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0041) << "\n"; - break; - case cmPolicies::OLD: - continue; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - hadFatalError = true; - break; // Issue fatal message. - } - } else { - hadFatalError = true; - } - } - if (!cmSystemTools::FileIsFullPath(li)) { - /* clang-format off */ - e << "Target \"" << target->GetName() << "\" " << prop << - " property contains relative path:\n" - " \"" << li << "\""; - /* clang-format on */ - target->GetLocalGenerator()->IssueMessage(messageType, e.str()); - } - bool inBinary = isSubDirectory(li, topBinaryDir); - bool inSource = isSubDirectory(li, topSourceDir); - if (isSubDirectory(li, installDir)) { - // The include directory is inside the install tree. If the - // install tree is not inside the source tree or build tree then - // fall through to the checks below that the include directory is not - // also inside the source tree or build tree. - bool shouldContinue = - (!inBinary || isSubDirectory(installDir, topBinaryDir)) && - (!inSource || isSubDirectory(installDir, topSourceDir)); - - if (prop == "INTERFACE_INCLUDE_DIRECTORIES") { - if (!shouldContinue) { - switch (target->GetPolicyStatusCMP0052()) { - case cmPolicies::WARN: { - std::ostringstream s; - s << cmPolicies::GetPolicyWarning(cmPolicies::CMP0052) << "\n"; - s << "Directory:\n \"" << li - << "\"\nin " - "INTERFACE_INCLUDE_DIRECTORIES of target \"" - << target->GetName() - << "\" is a subdirectory of the install " - "directory:\n \"" - << installDir - << "\"\nhowever it is also " - "a subdirectory of the " - << (inBinary ? "build" : "source") << " tree:\n \"" - << (inBinary ? topBinaryDir : topSourceDir) << "\"\n"; - target->GetLocalGenerator()->IssueMessage( - MessageType::AUTHOR_WARNING, s.str()); - CM_FALLTHROUGH; - } - case cmPolicies::OLD: - shouldContinue = true; - break; - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::NEW: - break; - } - } - } - if (shouldContinue) { - continue; - } - } - if (inBinary) { - /* clang-format off */ - e << "Target \"" << target->GetName() << "\" " << prop << - " property contains path:\n" - " \"" << li << "\"\nwhich is prefixed in the build directory."; - /* clang-format on */ - target->GetLocalGenerator()->IssueMessage(messageType, e.str()); - } - if (!inSourceBuild) { - if (inSource) { - e << "Target \"" << target->GetName() << "\" " << prop - << " property contains path:\n" - " \"" - << li << "\"\nwhich is prefixed in the source directory."; - target->GetLocalGenerator()->IssueMessage(messageType, e.str()); - } - } - } - return !hadFatalError; -} - -static void prefixItems(std::string& exportDirs) +void cmExportFileGenerator::AddImportPrefix(std::string& exportDirs) const { std::vector<std::string> entries; cmGeneratorExpression::Split(exportDirs, entries); exportDirs.clear(); - const char* sep = ""; + char const* sep = ""; + cm::string_view const& prefixWithSlash = this->GetImportPrefixWithSlash(); for (std::string const& e : entries) { exportDirs += sep; sep = ";"; if (!cmSystemTools::FileIsFullPath(e) && - e.find("${_IMPORT_PREFIX}") == std::string::npos) { - exportDirs += "${_IMPORT_PREFIX}/"; + !cmHasPrefix(e, prefixWithSlash)) { + exportDirs += prefixWithSlash; } exportDirs += e; } } -void cmExportFileGenerator::PopulateSourcesInterface( - cmGeneratorTarget const* gt, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - assert(preprocessRule == cmGeneratorExpression::InstallInterface); - - const char* propName = "INTERFACE_SOURCES"; - cmValue input = gt->GetProperty(propName); - - if (!input) { - return; - } - - if (input->empty()) { - properties[propName].clear(); - return; - } - - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule, true); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, gt); - - if (!checkInterfaceDirs(prepro, gt, propName)) { - return; - } - properties[propName] = prepro; - } -} - -void cmExportFileGenerator::PopulateIncludeDirectoriesInterface( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties, cmTargetExport const& te, - std::string& includesDestinationDirs) -{ - assert(preprocessRule == cmGeneratorExpression::InstallInterface); - - includesDestinationDirs.clear(); - - const char* propName = "INTERFACE_INCLUDE_DIRECTORIES"; - cmValue input = target->GetProperty(propName); - - cmGeneratorExpression ge(*target->Makefile->GetCMakeInstance()); - - std::string dirs = cmGeneratorExpression::Preprocess( - cmList::to_string(target->Target->GetInstallIncludeDirectoriesEntries(te)), - preprocessRule, true); - this->ReplaceInstallPrefix(dirs); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(dirs); - std::string exportDirs = - cge->Evaluate(target->GetLocalGenerator(), "", target); - - if (cge->GetHadContextSensitiveCondition()) { - cmLocalGenerator* lg = target->GetLocalGenerator(); - std::ostringstream e; - e << "Target \"" << target->GetName() - << "\" is installed with " - "INCLUDES DESTINATION set to a context sensitive path. Paths which " - "depend on the configuration, policy values or the link interface " - "are " - "not supported. Consider using target_include_directories instead."; - lg->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return; - } - - if (!input && exportDirs.empty()) { - return; - } - if ((input && input->empty()) && exportDirs.empty()) { - // Set to empty - properties[propName].clear(); - return; - } - - prefixItems(exportDirs); - includesDestinationDirs = exportDirs; - - std::string includes = (input ? *input : ""); - const char* sep = input ? ";" : ""; - includes += sep + exportDirs; - std::string prepro = - cmGeneratorExpression::Preprocess(includes, preprocessRule, true); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, target); - - if (!checkInterfaceDirs(prepro, target, propName)) { - return; - } - properties[propName] = prepro; - } -} - -void cmExportFileGenerator::PopulateLinkDependsInterface( - cmGeneratorTarget const* gt, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - assert(preprocessRule == cmGeneratorExpression::InstallInterface); - - const char* propName = "INTERFACE_LINK_DEPENDS"; - cmValue input = gt->GetProperty(propName); - - if (!input) { - return; - } - - if (input->empty()) { - properties[propName].clear(); - return; - } - - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule, true); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, gt); - - if (!checkInterfaceDirs(prepro, gt, propName)) { - return; - } - properties[propName] = prepro; - } -} - -void cmExportFileGenerator::PopulateLinkDirectoriesInterface( - cmGeneratorTarget const* gt, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - assert(preprocessRule == cmGeneratorExpression::InstallInterface); - - const char* propName = "INTERFACE_LINK_DIRECTORIES"; - cmValue input = gt->GetProperty(propName); - - if (!input) { - return; - } - - if (input->empty()) { - properties[propName].clear(); - return; - } - - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule, true); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, gt); - - if (!checkInterfaceDirs(prepro, gt, propName)) { - return; - } - properties[propName] = prepro; - } -} - -void cmExportFileGenerator::PopulateInterfaceProperty( - const std::string& propName, cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - this->PopulateInterfaceProperty(propName, propName, target, preprocessRule, - properties); -} - -static void getPropertyContents(cmGeneratorTarget const* tgt, - const std::string& prop, - std::set<std::string>& ifaceProperties) +namespace { +void getPropertyContents(cmGeneratorTarget const* tgt, std::string const& prop, + std::set<std::string>& ifaceProperties) { cmValue p = tgt->GetProperty(prop); if (!p) { @@ -530,9 +234,9 @@ static void getPropertyContents(cmGeneratorTarget const* tgt, ifaceProperties.insert(content.begin(), content.end()); } -static void getCompatibleInterfaceProperties( - cmGeneratorTarget const* target, std::set<std::string>& ifaceProperties, - const std::string& config) +void getCompatibleInterfaceProperties(cmGeneratorTarget const* target, + std::set<std::string>& ifaceProperties, + std::string const& config) { if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) { // object libraries have no link information, so nothing to compute @@ -551,7 +255,7 @@ static void getCompatibleInterfaceProperties( return; } - const cmComputeLinkInformation::ItemVector& deps = info->GetItems(); + cmComputeLinkInformation::ItemVector const& deps = info->GetItems(); for (auto const& dep : deps) { if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { @@ -567,9 +271,10 @@ static void getCompatibleInterfaceProperties( ifaceProperties); } } +} void cmExportFileGenerator::PopulateCompatibleInterfaceProperties( - cmGeneratorTarget const* gtarget, ImportPropertyMap& properties) + cmGeneratorTarget const* gtarget, ImportPropertyMap& properties) const { this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_BOOL", gtarget, properties); @@ -626,22 +331,6 @@ void cmExportFileGenerator::PopulateCustomTransitiveInterfaceProperties( } } -void cmExportFileGenerator::GenerateInterfaceProperties( - const cmGeneratorTarget* target, std::ostream& os, - const ImportPropertyMap& 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"; - } -} - bool cmExportFileGenerator::AddTargetNamespace(std::string& input, cmGeneratorTarget const* target, cmLocalGenerator const* lg) @@ -805,74 +494,14 @@ void cmExportFileGenerator::ResolveTargetsInGeneratorExpression( } } -void cmExportFileGenerator::ReplaceInstallPrefix(std::string& /*unused*/) +void cmExportFileGenerator::ReplaceInstallPrefix(std::string& /*unused*/) const { // Do nothing } -void cmExportFileGenerator::SetImportLinkInterface( - const std::string& 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; - } - - const bool 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 cmExportFileGenerator::SetImportDetailProperties( - const std::string& config, std::string const& suffix, - cmGeneratorTarget* target, ImportPropertyMap& properties) + std::string const& config, std::string const& suffix, + cmGeneratorTarget const* target, ImportPropertyMap& properties) { // Get the makefile in which to lookup target information. cmMakefile* mf = target->Makefile; @@ -943,20 +572,22 @@ void cmExportFileGenerator::SetImportDetailProperties( } } -static std::string const& asString(std::string const& l) +namespace { +std::string const& asString(std::string const& l) { return l; } -static std::string const& asString(cmLinkItem const& l) +std::string const& asString(cmLinkItem const& l) { return l.AsStr(); } +} template <typename T> void cmExportFileGenerator::SetImportLinkProperty( std::string const& suffix, cmGeneratorTarget const* target, - const std::string& propName, std::vector<T> const& entries, + std::string const& propName, std::vector<T> const& entries, ImportPropertyMap& properties, ImportLinkPropertyTargetNames targetNames) { // Skip the property if there are no entries. @@ -968,7 +599,7 @@ void cmExportFileGenerator::SetImportLinkProperty( // Construct the property value. std::string link_entries; - const char* sep = ""; + char const* sep = ""; for (T const& l : entries) { // Separate this from the previous entry. link_entries += sep; @@ -988,428 +619,17 @@ void cmExportFileGenerator::SetImportLinkProperty( properties[prop] = link_entries; } -void cmExportFileGenerator::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 >= 2.8.0 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 cmExportFileGenerator::GeneratePolicyFooterCode(std::ostream& os) -{ - os << "cmake_policy(POP)\n"; -} - -void cmExportFileGenerator::GenerateImportHeaderCode(std::ostream& os, - const std::string& 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 cmExportFileGenerator::GenerateImportFooterCode(std::ostream& os) -{ - os << "# Commands beyond this point should not need to know the version.\n" - << "set(CMAKE_IMPORT_FILE_VERSION)\n"; -} - -void cmExportFileGenerator::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 cmExportFileGenerator::GenerateExpectedTargetsCode( - std::ostream& os, const std::string& 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 cmExportFileGenerator::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 cmExportFileGenerator::GenerateImportPropertyCode( - std::ostream& os, const std::string& config, const std::string& suffix, - cmGeneratorTarget const* target, ImportPropertyMap const& properties, - const std::string& 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 cmExportFileGenerator::GenerateFindDependencyCalls(std::ostream& os) -{ - os << "include(CMakeFindDependencyMacro)\n"; - std::map<std::string, cmExportSet::PackageDependency> packageDependencies; - auto* exportSet = this->GetExportSet(); - if (exportSet) { - packageDependencies = exportSet->GetPackageDependencies(); - } +template void cmExportFileGenerator::SetImportLinkProperty<std::string>( + std::string const&, cmGeneratorTarget const*, std::string const&, + std::vector<std::string> const&, ImportPropertyMap& properties, + ImportLinkPropertyTargetNames); - 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(), - [](const std::pair<std::string, cmExportSet::PackageDependency>& lhs, - const std::pair<std::string, cmExportSet::PackageDependency>& 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 cmExportFileGenerator::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 cmExportFileGenerator::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 cmExportFileGenerator::GenerateImportedFileChecksCode( - std::ostream& os, cmGeneratorTarget* target, - ImportPropertyMap const& properties, - const std::set<std::string>& importedLocations, - const std::string& 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"; -} +template void cmExportFileGenerator::SetImportLinkProperty<cmLinkItem>( + std::string const&, cmGeneratorTarget const*, std::string const&, + std::vector<cmLinkItem> const&, ImportPropertyMap& properties, + ImportLinkPropertyTargetNames); +namespace { enum class ExportWhen { Defined, @@ -1423,7 +643,6 @@ enum class PropertyType IncludePaths, }; -namespace { bool PropertyTypeIsForPaths(PropertyType pt) { switch (pt) { @@ -1437,18 +656,6 @@ bool PropertyTypeIsForPaths(PropertyType pt) } } -struct ModuleTargetPropertyTable -{ - cm::static_string_view Name; - ExportWhen Cond; -}; - -struct ModulePropertyTable -{ - cm::static_string_view Name; - PropertyType Type; -}; - bool cmExportFileGenerator::PopulateCxxModuleExportProperties( cmGeneratorTarget const* gte, ImportPropertyMap& properties, cmGeneratorExpression::PreprocessContext ctx, @@ -1458,7 +665,13 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( return true; } - const ModuleTargetPropertyTable exportedDirectModuleProperties[] = { + struct ModuleTargetPropertyTable + { + cm::static_string_view Name; + ExportWhen Cond; + }; + + ModuleTargetPropertyTable const exportedDirectModuleProperties[] = { { "CXX_EXTENSIONS"_s, ExportWhen::Defined }, // Always define this property as it is an intrinsic property of the target // and should not be inherited from the in-scope `CMAKE_CXX_MODULE_STD` @@ -1484,7 +697,13 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( } } - const ModulePropertyTable exportedModuleProperties[] = { + struct ModulePropertyTable + { + cm::static_string_view Name; + PropertyType Type; + }; + + ModulePropertyTable const exportedModuleProperties[] = { { "INCLUDE_DIRECTORIES"_s, PropertyType::IncludePaths }, { "COMPILE_DEFINITIONS"_s, PropertyType::Strings }, { "COMPILE_OPTIONS"_s, PropertyType::Strings }, @@ -1505,7 +724,7 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( if (ctx == cmGeneratorExpression::InstallInterface && PropertyTypeIsForPaths(propEntry.Type)) { this->ReplaceInstallPrefix(properties[exportedPropName]); - prefixItems(properties[exportedPropName]); + this->AddImportPrefix(properties[exportedPropName]); if (propEntry.Type == PropertyType::IncludePaths && !includesDestinationDirs.empty()) { if (!properties[exportedPropName].empty()) { @@ -1517,7 +736,7 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( } } - const cm::static_string_view exportedLinkModuleProperties[] = { + cm::static_string_view const exportedLinkModuleProperties[] = { "LINK_LIBRARIES"_s, }; for (auto const& propName : exportedLinkModuleProperties) { @@ -1531,8 +750,8 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( auto const exportedPropName = cmStrCat("IMPORTED_CXX_MODULES_", propName); auto value = cmGeneratorExpression::Preprocess(*prop, ctx); - this->ResolveTargetsInGeneratorExpressions( - value, gte, cmExportFileGenerator::ReplaceFreeTargets); + this->ResolveTargetsInGeneratorExpressions(value, gte, + ReplaceFreeTargets); properties[exportedPropName] = value; } } @@ -1542,9 +761,9 @@ bool cmExportFileGenerator::PopulateCxxModuleExportProperties( bool cmExportFileGenerator::PopulateExportProperties( cmGeneratorTarget const* gte, ImportPropertyMap& properties, - std::string& errorMessage) + std::string& errorMessage) const { - const auto& targetProperties = gte->Target->GetProperties(); + auto const& targetProperties = gte->Target->GetProperties(); if (cmValue exportProperties = targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) { for (auto& prop : cmList{ *exportProperties }) { @@ -1579,95 +798,3 @@ bool cmExportFileGenerator::PopulateExportProperties( } return true; } - -void cmExportFileGenerator::GenerateTargetFileSets(cmGeneratorTarget* gte, - std::ostream& os, - cmTargetExport* 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"; - } -} - -void cmExportFileGenerator::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"; - - // Get the path to the file we're going to write. - std::string path = this->MainImportFile; - path = cmSystemTools::GetFilenamePath(path); - auto trampoline_path = - cmStrCat(path, '/', cxx_module_dirname, "/cxx-modules-", name, ".cmake"); - - // Include all configuration-specific include files. - cmGeneratedFileStream ap(trampoline_path, true); - ap.SetCopyIfDifferent(true); - - this->GenerateCxxModuleConfigInformation(name, ap); -} - -void cmExportFileGenerator::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; - } -} diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index f275a12..89295f5 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -10,16 +10,13 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmGeneratorExpression.h" -#include "cmStateTypes.h" -#include "cmVersion.h" -#include "cmVersionConfig.h" class cmExportSet; -class cmFileSet; class cmGeneratorTarget; class cmLocalGenerator; -class cmTargetExport; #define STRINGIFY_HELPER(X) #X #define STRINGIFY(X) STRINGIFY_HELPER(X) @@ -32,12 +29,9 @@ class cmTargetExport; : #major "." #minor ".0") /** \class cmExportFileGenerator - * \brief Generate a file exporting targets from a build or install tree. + * \brief Generate files exporting targets from a build or install tree. * - * cmExportFileGenerator is the superclass for - * cmExportBuildFileGenerator and cmExportInstallFileGenerator. It - * contains common code generation routines for the two kinds of - * export implementations. + * cmExportFileGenerator is the interface class for generating export files. */ class cmExportFileGenerator { @@ -46,65 +40,28 @@ public: virtual ~cmExportFileGenerator() = default; /** Set the full path to the export file to generate. */ - void SetExportFile(const char* mainFile); - const std::string& GetMainExportFileName() const; + void SetExportFile(char const* mainFile); + std::string const& GetMainExportFileName() const; /** Set the namespace in which to place exported target names. */ - void SetNamespace(const std::string& ns) { this->Namespace = ns; } + void SetNamespace(std::string const& ns) { this->Namespace = ns; } std::string GetNamespace() const { return this->Namespace; } - void SetExportOld(bool exportOld) { this->ExportOld = exportOld; } - /** Add a configuration to be exported. */ - void AddConfiguration(const std::string& config); + void AddConfiguration(std::string const& config); - /** Actually generate the export file. Returns whether there was an - error. */ + /** Create and actually generate the export file. Returns whether there was + an error. */ bool GenerateImportFile(); - void SetExportPackageDependencies(bool exportPackageDependencies) - { - this->ExportPackageDependencies = exportPackageDependencies; - } - protected: using ImportPropertyMap = std::map<std::string, std::string>; - // Generate per-configuration target information to the given output - // stream. - void GenerateImportConfig(std::ostream& os, const std::string& config); - - // Methods to implement export file code generation. - virtual void GeneratePolicyHeaderCode(std::ostream& os); - virtual void GeneratePolicyFooterCode(std::ostream& os); - virtual void GenerateImportHeaderCode(std::ostream& os, - const std::string& config = ""); - virtual void GenerateImportFooterCode(std::ostream& os); - void GenerateImportVersionCode(std::ostream& os); - virtual void GenerateImportTargetCode(std::ostream& os, - cmGeneratorTarget const* target, - cmStateEnums::TargetType targetType); - virtual void GenerateImportPropertyCode( - std::ostream& os, const std::string& config, const std::string& suffix, - cmGeneratorTarget const* target, ImportPropertyMap const& properties, - const std::string& importedXcFrameworkLocation); - virtual void GenerateImportedFileChecksCode( - std::ostream& os, cmGeneratorTarget* target, - ImportPropertyMap const& properties, - const std::set<std::string>& importedLocations, - const std::string& importedXcFrameworkLocation); - virtual void GenerateImportedFileCheckLoop(std::ostream& os); - virtual void GenerateMissingTargetsCheckCode(std::ostream& os); - virtual void GenerateFindDependencyCalls(std::ostream& os); - - virtual void GenerateExpectedTargetsCode(std::ostream& os, - const std::string& expectedTargets); - // Collect properties with detailed information about targets beyond // their location on disk. - void SetImportDetailProperties(const std::string& config, + void SetImportDetailProperties(std::string const& config, std::string const& suffix, - cmGeneratorTarget* target, + cmGeneratorTarget const* target, ImportPropertyMap& properties); enum class ImportLinkPropertyTargetNames @@ -115,17 +72,25 @@ protected: template <typename T> void SetImportLinkProperty(std::string const& suffix, cmGeneratorTarget const* target, - const std::string& propName, + std::string const& propName, std::vector<T> const& entries, ImportPropertyMap& properties, ImportLinkPropertyTargetNames targetNames); + /** Generate the export file to the given output stream. Returns whether + there was an error. */ + virtual bool GenerateImportFile(std::ostream& os) = 0; + /** Each subclass knows how to generate its kind of export file. */ virtual bool GenerateMainFile(std::ostream& os) = 0; + /** Generate per-configuration target information to the given output + stream. */ + void GenerateImportConfig(std::ostream& os, std::string const& config); + /** Each subclass knows where the target files are located. */ virtual void GenerateImportTargetsConfig(std::ostream& os, - const std::string& config, + std::string const& config, std::string const& suffix) = 0; /** Each subclass knows how to deal with a target that is missing from an @@ -133,47 +98,33 @@ protected: virtual void HandleMissingTarget(std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) = 0; - void PopulateInterfaceProperty(const std::string&, + + /** Complain when a duplicate target is encountered. */ + virtual void ComplainAboutDuplicateTarget( + std::string const& targetName) const = 0; + + virtual cm::string_view GetImportPrefixWithSlash() const = 0; + + void AddImportPrefix(std::string& exportDirs) const; + + void PopulateInterfaceProperty(std::string const& propName, + cmGeneratorTarget const* target, + ImportPropertyMap& properties) const; + void PopulateInterfaceProperty(std::string const& propName, cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); bool PopulateInterfaceLinkLibrariesProperty( cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); - void PopulateInterfaceProperty(const std::string& propName, - cmGeneratorTarget const* target, - ImportPropertyMap& properties); - void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target, - ImportPropertyMap& properties); - void PopulateCustomTransitiveInterfaceProperties( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties); - virtual void GenerateInterfaceProperties( - cmGeneratorTarget const* target, std::ostream& os, - const ImportPropertyMap& properties); - void PopulateIncludeDirectoriesInterface( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties, cmTargetExport const& te, - std::string& includesDestinationDirs); - void PopulateSourcesInterface( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties); - void PopulateLinkDirectoriesInterface( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties); - void PopulateLinkDependsInterface( + + bool PopulateInterfaceProperties( cmGeneratorTarget const* target, + std::string const& includesDestinationDirs, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties); - void SetImportLinkInterface( - const std::string& config, std::string const& suffix, - cmGeneratorExpression::PreprocessContext preprocessRule, - cmGeneratorTarget const* target, ImportPropertyMap& properties); + virtual void ReportError(std::string const& errorMessage) const = 0; enum FreeTargetsReplace { @@ -185,36 +136,26 @@ protected: std::string& input, cmGeneratorTarget const* target, FreeTargetsReplace replace = NoReplaceFreeTargets); - bool PopulateCxxModuleExportProperties( - cmGeneratorTarget const* gte, ImportPropertyMap& properties, - cmGeneratorExpression::PreprocessContext ctx, - std::string const& includesDestinationDirs, std::string& errorMessage); - bool PopulateExportProperties(cmGeneratorTarget const* gte, - ImportPropertyMap& properties, - std::string& errorMessage); + virtual cmExportSet* GetExportSet() const { return nullptr; } - void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, - cmTargetExport* te = nullptr); + virtual void ReplaceInstallPrefix(std::string& input) const; - void GenerateCxxModuleInformation(std::string const& name, std::ostream& os); + virtual std::string InstallNameDir(cmGeneratorTarget const* target, + std::string const& config) = 0; - virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, - cmFileSet* fileSet, - cmTargetExport* te) = 0; - virtual std::string GetFileSetFiles(cmGeneratorTarget* gte, - cmFileSet* fileSet, - cmTargetExport* te) = 0; + /** Get the temporary location of the config-agnostic C++ module file. */ + virtual std::string GetCxxModuleFile(std::string const& name) const = 0; - virtual cmExportSet* GetExportSet() const { return nullptr; } + virtual std::string GetCxxModulesDirectory() const = 0; + virtual void GenerateCxxModuleConfigInformation(std::string const&, + std::ostream& os) const = 0; - void SetRequiredCMakeVersion(unsigned int major, unsigned int minor, - unsigned int patch); + bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target, + cmLocalGenerator const* lg); // The namespace in which the exports are placed in the generated file. std::string Namespace; - bool ExportOld; - // The set of configurations to export. std::vector<std::string> Configurations; @@ -223,40 +164,47 @@ protected: std::string FileDir; std::string FileBase; std::string FileExt; - bool AppendMode; + bool AppendMode = false; // The set of targets included in the export. - std::set<cmGeneratorTarget*> ExportedTargets; + std::set<cmGeneratorTarget const*> ExportedTargets; std::vector<std::string> MissingTargets; std::set<cmGeneratorTarget const*> ExternalTargets; - unsigned int RequiredCMakeVersionMajor = 2; - unsigned int RequiredCMakeVersionMinor = 8; - unsigned int RequiredCMakeVersionPatch = 3; - - bool ExportPackageDependencies = false; - private: - void PopulateInterfaceProperty(const std::string&, const std::string&, + void PopulateInterfaceProperty(std::string const& propName, + std::string const& outputName, cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext, ImportPropertyMap& properties); - bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target, - cmLocalGenerator const* lg); + void PopulateCompatibleInterfaceProperties( + cmGeneratorTarget const* target, ImportPropertyMap& properties) const; + void PopulateCustomTransitiveInterfaceProperties( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + bool PopulateCxxModuleExportProperties( + cmGeneratorTarget const* gte, ImportPropertyMap& properties, + cmGeneratorExpression::PreprocessContext ctx, + std::string const& includesDestinationDirs, std::string& errorMessage); + bool PopulateExportProperties(cmGeneratorTarget const* gte, + ImportPropertyMap& properties, + std::string& errorMessage) const; void ResolveTargetsInGeneratorExpression(std::string& input, cmGeneratorTarget const* target, cmLocalGenerator const* lg); +}; - virtual void ReplaceInstallPrefix(std::string& input); - - virtual std::string InstallNameDir(cmGeneratorTarget const* target, - const std::string& config) = 0; +extern template void cmExportFileGenerator::SetImportLinkProperty<std::string>( + std::string const&, cmGeneratorTarget const*, std::string const&, + std::vector<std::string> const&, ImportPropertyMap& properties, + ImportLinkPropertyTargetNames); - virtual std::string GetCxxModulesDirectory() const = 0; - virtual void GenerateCxxModuleConfigInformation(std::string const&, - std::ostream& os) const = 0; -}; +extern template void cmExportFileGenerator::SetImportLinkProperty<cmLinkItem>( + std::string const&, cmGeneratorTarget const*, std::string const&, + std::vector<cmLinkItem> const&, ImportPropertyMap& properties, + ImportLinkPropertyTargetNames); diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx index eaa85f3..c9c2f4e 100644 --- a/Source/cmExportInstallAndroidMKGenerator.cxx +++ b/Source/cmExportInstallAndroidMKGenerator.cxx @@ -4,14 +4,15 @@ #include <cstddef> #include <memory> -#include <ostream> +#include <sstream> #include <vector> -#include "cmExportBuildAndroidMKGenerator.h" #include "cmExportSet.h" +#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmInstallExportGenerator.h" #include "cmInstallTargetGenerator.h" +#include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -24,8 +25,55 @@ cmExportInstallAndroidMKGenerator::cmExportInstallAndroidMKGenerator( { } +void cmExportInstallAndroidMKGenerator::ReportDuplicateTarget( + std::string const& targetName) const +{ + std::ostringstream e; + e << "install(EXPORT_ANDROID_MK \"" << this->GetExportSet()->GetName() + << "\" ...) " + << "includes target \"" << targetName + << "\" more than once in the export set."; + this->ReportError(e.str()); +} + +bool cmExportInstallAndroidMKGenerator::GenerateMainFile(std::ostream& os) +{ + std::vector<cmTargetExport const*> allTargets; + { + auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); }; + + if (!this->CollectExports(visitor)) { + return false; + } + } + + // Create all the imported targets. + for (cmTargetExport const* te : allTargets) { + cmGeneratorTarget const* gt = te->Target; + + this->GenerateImportTargetCode(os, gt, this->GetExportTargetType(te)); + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(te, properties)) { + return false; + } + + bool const newCMP0022Behavior = + gt->GetPolicyStatusCMP0022() != cmPolicies::WARN && + gt->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) { + this->PopulateInterfaceLinkLibrariesProperty( + gt, cmGeneratorExpression::InstallInterface, properties); + } + + this->GenerateInterfaceProperties(gt, os, properties); + } + + return true; +} + void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode( - std::ostream& os, const std::string&) + std::ostream& os, std::string const&) { std::string installDir = this->IEGen->GetDestination(); os << "LOCAL_PATH := $(call my-dir)\n"; @@ -53,10 +101,6 @@ void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode( } } -void cmExportInstallAndroidMKGenerator::GenerateImportFooterCode(std::ostream&) -{ -} - void cmExportInstallAndroidMKGenerator::GenerateImportTargetCode( std::ostream& os, cmGeneratorTarget const* target, cmStateEnums::TargetType /*targetType*/) @@ -73,61 +117,3 @@ void cmExportInstallAndroidMKGenerator::GenerateImportTargetCode( } os << target->GetFullName(config) << "\n"; } - -void cmExportInstallAndroidMKGenerator::GenerateExpectedTargetsCode( - std::ostream&, const std::string&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateImportPropertyCode( - std::ostream&, const std::string&, const std::string&, - cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateMissingTargetsCheckCode( - std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateInterfaceProperties( - cmGeneratorTarget const* target, std::ostream& os, - const ImportPropertyMap& properties) -{ - std::string config; - if (!this->Configurations.empty()) { - config = this->Configurations[0]; - } - cmExportBuildAndroidMKGenerator::GenerateInterfaceProperties( - target, os, properties, cmExportBuildAndroidMKGenerator::INSTALL, config); -} - -void cmExportInstallAndroidMKGenerator::LoadConfigFiles(std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateImportPrefix(std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::CleanupTemporaryVariables( - std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateImportedFileCheckLoop( - std::ostream&) -{ -} - -void cmExportInstallAndroidMKGenerator::GenerateImportedFileChecksCode( - std::ostream&, cmGeneratorTarget*, ImportPropertyMap const&, - const std::set<std::string>&, const std::string&) -{ -} - -bool cmExportInstallAndroidMKGenerator::GenerateImportFileConfig( - const std::string&) -{ - return true; -} diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h index b1778ef..8fa3d3e 100644 --- a/Source/cmExportInstallAndroidMKGenerator.h +++ b/Source/cmExportInstallAndroidMKGenerator.h @@ -5,9 +5,10 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <iosfwd> -#include <set> #include <string> +#include <vector> +#include "cmExportAndroidMKGenerator.h" #include "cmExportInstallFileGenerator.h" #include "cmStateTypes.h" @@ -15,52 +16,49 @@ class cmGeneratorTarget; class cmInstallExportGenerator; /** \class cmExportInstallAndroidMKGenerator - * \brief Generate a file exporting targets from an install tree. + * \brief Generate files exporting targets from an install tree. * * cmExportInstallAndroidMKGenerator generates files exporting targets from - * install an installation tree. The files are placed in a temporary - * location for installation by cmInstallExportGenerator. The file format - * is for the ndk build system and is a makefile fragment specifying prebuilt - * libraries to the ndk build system. + * an installation tree. The files are placed in a temporary location for + * installation by cmInstallExportGenerator. The file format is for the ndk + * build system and is a makefile fragment specifying prebuilt libraries to the + * ndk build system. * * This is used to implement the INSTALL(EXPORT_ANDROID_MK) command. */ -class cmExportInstallAndroidMKGenerator : public cmExportInstallFileGenerator +class cmExportInstallAndroidMKGenerator + : public cmExportAndroidMKGenerator + , public cmExportInstallFileGenerator { public: /** Construct with the export installer that will install the files. */ cmExportInstallAndroidMKGenerator(cmInstallExportGenerator* iegen); + std::string GetConfigImportFileGlob() const override { return {}; } + protected: + GenerateType GetGenerateType() const override { return INSTALL; } + // Implement virtual methods from the superclass. - void GeneratePolicyHeaderCode(std::ostream&) override {} - void GeneratePolicyFooterCode(std::ostream&) override {} + void ReportDuplicateTarget(std::string const& targetName) const; + bool GenerateMainFile(std::ostream& os) override; void GenerateImportHeaderCode(std::ostream& os, - const std::string& config = "") override; - void GenerateImportFooterCode(std::ostream& os) override; + std::string const& config = "") override; void GenerateImportTargetCode( std::ostream& os, cmGeneratorTarget const* target, cmStateEnums::TargetType /*targetType*/) override; - void GenerateExpectedTargetsCode( - std::ostream& os, const std::string& expectedTargets) override; - void GenerateImportPropertyCode( - std::ostream& os, const std::string& config, const std::string& suffix, - cmGeneratorTarget const* target, ImportPropertyMap const& properties, - const std::string& importedXcFrameworkLocation) override; - void GenerateMissingTargetsCheckCode(std::ostream& os) override; - void GenerateFindDependencyCalls(std::ostream&) override {} - void GenerateInterfaceProperties( - cmGeneratorTarget const* target, std::ostream& os, - const ImportPropertyMap& properties) override; - void GenerateImportPrefix(std::ostream& os) override; - void LoadConfigFiles(std::ostream&) override; - void CleanupTemporaryVariables(std::ostream&) override; - void GenerateImportedFileCheckLoop(std::ostream& os) override; - void GenerateImportedFileChecksCode( - std::ostream& os, cmGeneratorTarget* target, - ImportPropertyMap const& properties, - const std::set<std::string>& importedLocations, - const std::string& importedXcFrameworkLocation) override; - bool GenerateImportFileConfig(const std::string& config) override; + + void ComplainAboutMissingTarget(cmGeneratorTarget const* depender, + cmGeneratorTarget const* dependee, + std::vector<std::string> const& namespaces); + + void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, + std::string const& suffix) override + { + this->cmExportAndroidMKGenerator::GenerateImportTargetsConfig(os, config, + suffix); + } + + std::string GetCxxModulesDirectory() const override { return {}; } }; diff --git a/Source/cmExportInstallCMakeConfigGenerator.cxx b/Source/cmExportInstallCMakeConfigGenerator.cxx new file mode 100644 index 0000000..e2185ed --- /dev/null +++ b/Source/cmExportInstallCMakeConfigGenerator.cxx @@ -0,0 +1,507 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmExportInstallCMakeConfigGenerator.h" + +#include <algorithm> +#include <map> +#include <memory> +#include <set> +#include <sstream> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmExportSet.h" +#include "cmFileSet.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallFileSetGenerator.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 "cmTargetExport.h" +#include "cmValue.h" + +cmExportInstallCMakeConfigGenerator::cmExportInstallCMakeConfigGenerator( + cmInstallExportGenerator* iegen) + : cmExportInstallFileGenerator(iegen) +{ +} + +std::string cmExportInstallCMakeConfigGenerator::GetConfigImportFileGlob() + const +{ + std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt); + return glob; +} + +bool cmExportInstallCMakeConfigGenerator::GenerateMainFile(std::ostream& os) +{ + std::vector<cmTargetExport const*> allTargets; + { + std::string expectedTargets; + std::string sep; + auto visitor = [&](cmTargetExport const* te) { + allTargets.push_back(te); + expectedTargets += sep + this->Namespace + te->Target->GetExportName(); + sep = " "; + }; + + if (!this->CollectExports(visitor)) { + return false; + } + + this->GenerateExpectedTargetsCode(os, expectedTargets); + } + + // Compute the relative import prefix for the file + this->GenerateImportPrefix(os); + + bool requiresConfigFiles = false; + // Create all the imported targets. + for (cmTargetExport const* te : allTargets) { + cmGeneratorTarget* gt = te->Target; + cmStateEnums::TargetType targetType = this->GetExportTargetType(te); + + requiresConfigFiles = + requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY; + + this->GenerateImportTargetCode(os, gt, targetType); + + ImportPropertyMap properties; + if (!this->PopulateInterfaceProperties(te, properties)) { + return false; + } + + bool const newCMP0022Behavior = + gt->GetPolicyStatusCMP0022() != cmPolicies::WARN && + gt->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) { + if (this->PopulateInterfaceLinkLibrariesProperty( + gt, cmGeneratorExpression::InstallInterface, properties) && + !this->ExportOld) { + this->SetRequiredCMakeVersion(2, 8, 12); + } + } + if (targetType == cmStateEnums::INTERFACE_LIBRARY) { + this->SetRequiredCMakeVersion(3, 0, 0); + } + if (gt->GetProperty("INTERFACE_SOURCES")) { + // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1 + // can consume them. + this->SetRequiredCMakeVersion(3, 1, 0); + } + + this->GenerateInterfaceProperties(gt, os, properties); + + this->GenerateTargetFileSets(gt, os, te); + } + + this->LoadConfigFiles(os); + + bool result = true; + + std::string cxx_modules_name = this->GetExportSet()->GetName(); + this->GenerateCxxModuleInformation(cxx_modules_name, os); + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, + c)) { + result = false; + } + } + } + + this->CleanupTemporaryVariables(os); + this->GenerateImportedFileCheckLoop(os); + + // Generate an import file for each configuration. + // Don't do this if we only export INTERFACE_LIBRARY targets. + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportFileConfig(c)) { + result = false; + } + } + } + + this->GenerateMissingTargetsCheckCode(os); + + return result; +} + +void cmExportInstallCMakeConfigGenerator::GenerateImportPrefix( + std::ostream& os) +{ + // Set an _IMPORT_PREFIX variable for import location properties + // to reference if they are relative to the install prefix. + std::string installPrefix = + this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition( + "CMAKE_INSTALL_PREFIX"); + std::string const& expDest = this->IEGen->GetDestination(); + if (cmSystemTools::FileIsFullPath(expDest)) { + // The export file is being installed to an absolute path so the + // package is not relocatable. Use the configured install prefix. + /* clang-format off */ + os << + "# The installation prefix configured by this project.\n" + "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n" + "\n"; + /* clang-format on */ + } else { + // Add code to compute the installation prefix relative to the + // import file location. + std::string absDest = installPrefix + "/" + expDest; + std::string absDestS = absDest + "/"; + os << "# Compute the installation prefix relative to this file.\n" + << "get_filename_component(_IMPORT_PREFIX" + << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"; + if (cmHasLiteralPrefix(absDestS, "/lib/") || + cmHasLiteralPrefix(absDestS, "/lib64/") || + cmHasLiteralPrefix(absDestS, "/libx32/") || + cmHasLiteralPrefix(absDestS, "/usr/lib/") || + cmHasLiteralPrefix(absDestS, "/usr/lib64/") || + cmHasLiteralPrefix(absDestS, "/usr/libx32/")) { + // Handle "/usr move" symlinks created by some Linux distros. + /* clang-format off */ + os << + "# Use original install prefix when loaded through a\n" + "# cross-prefix symbolic link such as /lib -> /usr/lib.\n" + "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n" + "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n" + "if(_realCurr STREQUAL _realOrig)\n" + " set(_IMPORT_PREFIX \"" << absDest << "\")\n" + "endif()\n" + "unset(_realOrig)\n" + "unset(_realCurr)\n"; + /* clang-format on */ + } + std::string dest = expDest; + while (!dest.empty()) { + os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" " + "PATH)\n"; + dest = cmSystemTools::GetFilenamePath(dest); + } + os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n" + << " set(_IMPORT_PREFIX \"\")\n" + << "endif()\n" + << "\n"; + } +} + +void cmExportInstallCMakeConfigGenerator::CleanupTemporaryVariables( + std::ostream& os) +{ + /* clang-format off */ + os << "# Cleanup temporary variables.\n" + << "set(_IMPORT_PREFIX)\n" + << "\n"; + /* clang-format on */ +} + +void cmExportInstallCMakeConfigGenerator::LoadConfigFiles(std::ostream& os) +{ + // Now load per-configuration properties for them. + /* clang-format off */ + os << "# Load information for each installed configuration.\n" + << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/" + << this->GetConfigImportFileGlob() << "\")\n" + << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n" + << " include(\"${_cmake_config_file}\")\n" + << "endforeach()\n" + << "unset(_cmake_config_file)\n" + << "unset(_cmake_config_files)\n" + << "\n"; + /* clang-format on */ +} + +bool cmExportInstallCMakeConfigGenerator::GenerateImportFileConfig( + std::string const& config) +{ + // Skip configurations not enabled for this export. + if (!this->IEGen->InstallsForConfig(config)) { + return true; + } + + // Construct the name of the file to generate. + std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, '-'); + if (!config.empty()) { + fileName += cmSystemTools::LowerCase(config); + } else { + fileName += "noconfig"; + } + fileName += this->FileExt; + + // Open the output file to generate it. + cmGeneratedFileStream exportFileStream(fileName, true); + if (!exportFileStream) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + exportFileStream.SetCopyIfDifferent(true); + std::ostream& os = exportFileStream; + + // Start with the import file header. + this->GenerateImportHeaderCode(os, config); + + // Generate the per-config target information. + this->GenerateImportConfig(os, config); + + // End with the import file footer. + this->GenerateImportFooterCode(os); + + // Record this per-config import file. + this->ConfigImportFiles[config] = fileName; + + return true; +} + +void cmExportInstallCMakeConfigGenerator::GenerateImportTargetsConfig( + std::ostream& os, std::string const& config, std::string const& suffix) +{ + // Add each target in the set to the export. + for (std::unique_ptr<cmTargetExport> const& te : + this->GetExportSet()->GetTargetExports()) { + // Collect import properties for this target. + if (this->GetExportTargetType(te.get()) == + cmStateEnums::INTERFACE_LIBRARY) { + continue; + } + + ImportPropertyMap properties; + std::set<std::string> importedLocations; + + this->PopulateImportProperties(config, suffix, te.get(), properties, + importedLocations); + + // If any file location was set for the target add it to the + // import file. + if (!properties.empty()) { + cmGeneratorTarget const* const gtgt = te->Target; + std::string const importedXcFrameworkLocation = + this->GetImportXcFrameworkLocation(config, te.get()); + + this->SetImportLinkInterface(config, suffix, + cmGeneratorExpression::InstallInterface, + gtgt, properties); + + this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties, + importedXcFrameworkLocation); + this->GenerateImportedFileChecksCode( + os, gtgt, properties, importedLocations, importedXcFrameworkLocation); + } + } +} + +namespace { +bool EntryIsContextSensitive( + std::unique_ptr<cmCompiledGeneratorExpression> const& cge) +{ + return cge->GetHadContextSensitiveCondition(); +} +} + +std::string cmExportInstallCMakeConfigGenerator::GetFileSetDirectories( + cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te) +{ + std::vector<std::string> resultVector; + + auto configs = + gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); + auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination()); + + for (auto const& config : configs) { + auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte); + auto dest = cmOutputConverter::EscapeForCMake( + unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); + if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + dest = cmStrCat("${_IMPORT_PREFIX}/", dest); + } + + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (cge->GetHadContextSensitiveCondition() && type == "CXX_MODULES"_s) { + auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + + if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) { + resultVector.push_back( + cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); + } else { + resultVector.emplace_back(cmStrCat('"', dest, '"')); + break; + } + } + + return cmJoin(resultVector, " "); +} + +std::string cmExportInstallCMakeConfigGenerator::GetFileSetFiles( + cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport const* te) +{ + std::vector<std::string> resultVector; + + auto configs = + gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + + auto fileEntries = fileSet->CompileFileEntries(); + auto directoryEntries = fileSet->CompileDirectoryEntries(); + + cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance()); + auto destCge = + destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination()); + + for (auto const& config : configs) { + auto directories = fileSet->EvaluateDirectoryEntries( + directoryEntries, gte->LocalGenerator, config, gte); + + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + fileSet->EvaluateFileEntry(directories, files, entry, + gte->LocalGenerator, config, gte); + } + auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte); + auto dest = + cmStrCat(cmOutputConverter::EscapeForCMake( + unescapedDest, cmOutputConverter::WrapQuotes::NoWrap), + '/'); + if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + dest = cmStrCat("${_IMPORT_PREFIX}/", dest); + } + + bool const contextSensitive = destCge->GetHadContextSensitiveCondition() || + std::any_of(directoryEntries.begin(), directoryEntries.end(), + EntryIsContextSensitive) || + std::any_of(fileEntries.begin(), fileEntries.end(), + EntryIsContextSensitive); + + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && type == "CXX_MODULES"_s) { + auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + + for (auto const& it : files) { + auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/'); + for (auto const& filename : it.second) { + auto relFile = + cmStrCat(prefix, cmSystemTools::GetFilenameName(filename)); + auto escapedFile = + cmStrCat(dest, + cmOutputConverter::EscapeForCMake( + relFile, cmOutputConverter::WrapQuotes::NoWrap)); + if (contextSensitive && configs.size() != 1) { + resultVector.push_back( + cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\"")); + } else { + resultVector.emplace_back(cmStrCat('"', escapedFile, '"')); + } + } + } + + if (!(contextSensitive && configs.size() != 1)) { + break; + } + } + + return cmJoin(resultVector, " "); +} + +std::string cmExportInstallCMakeConfigGenerator::GetCxxModulesDirectory() const +{ + return IEGen->GetCxxModuleDirectory(); +} + +void cmExportInstallCMakeConfigGenerator::GenerateCxxModuleConfigInformation( + std::string const& name, std::ostream& os) const +{ + // Now load per-configuration properties for them. + /* clang-format off */ + os << "# Load information for each installed configuration.\n" + "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << "-*.cmake\")\n" + "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n" + " include(\"${_cmake_cxx_module_include}\")\n" + "endforeach()\n" + "unset(_cmake_cxx_module_include)\n" + "unset(_cmake_cxx_module_includes)\n"; + /* clang-format on */ +} + +bool cmExportInstallCMakeConfigGenerator:: + GenerateImportCxxModuleConfigTargetInclusion(std::string const& name, + std::string const& config) +{ + auto cxx_modules_dirname = this->GetCxxModulesDirectory(); + if (cxx_modules_dirname.empty()) { + return true; + } + + std::string filename_config = config; + if (filename_config.empty()) { + filename_config = "noconfig"; + } + + std::string const dest = + cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/'); + std::string fileName = + cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake"); + + cmGeneratedFileStream os(fileName, true); + if (!os) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + os.SetCopyIfDifferent(true); + + // Record this per-config import file. + this->ConfigCxxModuleFiles[config] = fileName; + + auto& prop_files = this->ConfigCxxModuleTargetFiles[config]; + for (auto const* tgt : this->ExportedTargets) { + // Only targets with C++ module sources will have a + // collator-generated install script. + if (!tgt->HaveCxx20ModuleSources()) { + continue; + } + + auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(), + '-', filename_config, ".cmake"); + prop_files.emplace_back(cmStrCat(dest, prop_filename)); + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n"; + } + + return true; +} diff --git a/Source/cmExportInstallCMakeConfigGenerator.h b/Source/cmExportInstallCMakeConfigGenerator.h new file mode 100644 index 0000000..2ef9af0 --- /dev/null +++ b/Source/cmExportInstallCMakeConfigGenerator.h @@ -0,0 +1,74 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <iosfwd> +#include <string> + +#include "cmExportCMakeConfigGenerator.h" +#include "cmExportInstallFileGenerator.h" + +class cmFileSet; +class cmGeneratorTarget; +class cmInstallExportGenerator; +class cmTargetExport; + +/** \class cmExportInstallCMakeConfigGenerator + * \brief Generate files exporting targets from an install tree. + * + * cmExportInstallCMakeConfigGenerator generates files exporting targets from + * an installation tree. The files are placed in a temporary location for + * installation by cmInstallExportGenerator. The file format is CMake's native + * package configuration format. + * + * One main file is generated that creates the imported targets and loads + * per-configuration files. Target locations and settings for each + * configuration are written to these per-configuration files. After + * installation the main file loads the configurations that have been + * installed. + * + * This is used to implement the INSTALL(EXPORT) command. + */ +class cmExportInstallCMakeConfigGenerator + : public cmExportCMakeConfigGenerator + , public cmExportInstallFileGenerator +{ +public: + /** Construct with the export installer that will install the + files. */ + cmExportInstallCMakeConfigGenerator(cmInstallExportGenerator* iegen); + + /** Compute the globbing expression used to load per-config import + files from the main file. */ + std::string GetConfigImportFileGlob() const override; + +protected: + // Implement virtual methods from the superclass. + bool GenerateMainFile(std::ostream& os) override; + void GenerateImportTargetsConfig(std::ostream& os, std::string const& config, + std::string const& suffix) override; + + /** Generate the relative import prefix. */ + virtual void GenerateImportPrefix(std::ostream&); + + /** Generate the relative import prefix. */ + virtual void LoadConfigFiles(std::ostream&); + + virtual void CleanupTemporaryVariables(std::ostream&); + + /** Generate a per-configuration file for the targets. */ + virtual bool GenerateImportFileConfig(std::string const& config); + + std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, + cmTargetExport const* te) override; + std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, + cmTargetExport const* te) override; + + std::string GetCxxModulesDirectory() const override; + void GenerateCxxModuleConfigInformation(std::string const&, + std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&, + std::string const&); +}; diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index f5f22ef..8738938 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -3,29 +3,21 @@ #include "cmExportInstallFileGenerator.h" #include <algorithm> +#include <cassert> +#include <cstddef> #include <memory> +#include <set> #include <sstream> -#include <utility> - -#include <cm/string_view> -#include <cmext/string_view> #include "cmExportSet.h" -#include "cmFileSet.h" -#include "cmGeneratedFileStream.h" -#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" -#include "cmInstallExportGenerator.h" -#include "cmInstallFileSetGenerator.h" #include "cmInstallTargetGenerator.h" #include "cmList.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" @@ -38,373 +30,75 @@ cmExportInstallFileGenerator::cmExportInstallFileGenerator( { } -std::string cmExportInstallFileGenerator::GetConfigImportFileGlob() +void cmExportInstallFileGenerator::ReplaceInstallPrefix( + std::string& input) const { - std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt); - return glob; + cmGeneratorExpression::ReplaceInstallPrefix(input, this->GetInstallPrefix()); } -bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) +void cmExportInstallFileGenerator::PopulateImportProperties( + std::string const& config, std::string const& suffix, + cmTargetExport const* targetExport, ImportPropertyMap& properties, + std::set<std::string>& importedLocations) { - std::vector<cmTargetExport*> allTargets; - { - std::string expectedTargets; - std::string sep; - for (std::unique_ptr<cmTargetExport> const& te : - this->IEGen->GetExportSet()->GetTargetExports()) { - if (te->NamelinkOnly) { - continue; - } - expectedTargets += sep + this->Namespace + te->Target->GetExportName(); - sep = " "; - if (this->ExportedTargets.insert(te->Target).second) { - allTargets.push_back(te.get()); - } else { - std::ostringstream e; - e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName() - << "\" ...) " - << "includes target \"" << te->Target->GetName() - << "\" more than once in the export set."; - cmSystemTools::Error(e.str()); - return false; - } - } - - this->GenerateExpectedTargetsCode(os, expectedTargets); - } - - // Compute the relative import prefix for the file - this->GenerateImportPrefix(os); - - bool requiresConfigFiles = false; - // Create all the imported targets. - for (cmTargetExport* te : allTargets) { - cmGeneratorTarget* gt = te->Target; - cmStateEnums::TargetType targetType = this->GetExportTargetType(te); - - requiresConfigFiles = - requiresConfigFiles || targetType != cmStateEnums::INTERFACE_LIBRARY; - - this->GenerateImportTargetCode(os, gt, targetType); - - ImportPropertyMap properties; - - std::string includesDestinationDirs; - this->PopulateIncludeDirectoriesInterface( - gt, cmGeneratorExpression::InstallInterface, properties, *te, - includesDestinationDirs); - this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt, - cmGeneratorExpression::InstallInterface, - properties); - this->PopulateLinkDirectoriesInterface( - gt, cmGeneratorExpression::InstallInterface, properties); - this->PopulateLinkDependsInterface( - gt, cmGeneratorExpression::InstallInterface, properties); - - std::string errorMessage; - if (!this->PopulateCxxModuleExportProperties( - gt, properties, cmGeneratorExpression::InstallInterface, - includesDestinationDirs, errorMessage)) { - cmSystemTools::Error(errorMessage); - return false; - } - - if (!this->PopulateExportProperties(gt, properties, errorMessage)) { - cmSystemTools::Error(errorMessage); - return false; - } - - const bool newCMP0022Behavior = - gt->GetPolicyStatusCMP0022() != cmPolicies::WARN && - gt->GetPolicyStatusCMP0022() != cmPolicies::OLD; - if (newCMP0022Behavior) { - if (this->PopulateInterfaceLinkLibrariesProperty( - gt, cmGeneratorExpression::InstallInterface, properties) && - !this->ExportOld) { - this->SetRequiredCMakeVersion(2, 8, 12); - } - } - if (targetType == cmStateEnums::INTERFACE_LIBRARY) { - this->SetRequiredCMakeVersion(3, 0, 0); - } - if (gt->GetProperty("INTERFACE_SOURCES")) { - // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1 - // can consume them. - this->SetRequiredCMakeVersion(3, 1, 0); - } - - this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt, - properties); - - this->PopulateCompatibleInterfaceProperties(gt, properties); - this->PopulateCustomTransitiveInterfaceProperties( - gt, cmGeneratorExpression::InstallInterface, properties); - - this->GenerateInterfaceProperties(gt, os, properties); - - this->GenerateTargetFileSets(gt, os, te); + this->SetImportLocationProperty(config, suffix, + targetExport->ArchiveGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->LibraryGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->RuntimeGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->ObjectsGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->FrameworkGenerator, properties, + importedLocations); + this->SetImportLocationProperty(config, suffix, + targetExport->BundleGenerator, properties, + importedLocations); + + // If any file location was set for the target add it to the + // import file. + if (!properties.empty()) { + // Get the rest of the target details. + cmGeneratorTarget const* const gtgt = targetExport->Target; + this->SetImportDetailProperties(config, suffix, gtgt, properties); + + // TODO: PUBLIC_HEADER_LOCATION + // This should wait until the build feature propagation stuff is done. + // Then this can be a propagated include directory. + // this->GenerateImportProperty(config, te->HeaderGenerator, properties); } - - this->LoadConfigFiles(os); - - bool result = true; - - std::string cxx_modules_name = this->IEGen->GetExportSet()->GetName(); - this->GenerateCxxModuleInformation(cxx_modules_name, os); - if (requiresConfigFiles) { - for (std::string const& c : this->Configurations) { - if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, - c)) { - result = false; - } - } - } - - this->CleanupTemporaryVariables(os); - this->GenerateImportedFileCheckLoop(os); - - // Generate an import file for each configuration. - // Don't do this if we only export INTERFACE_LIBRARY targets. - if (requiresConfigFiles) { - for (std::string const& c : this->Configurations) { - if (!this->GenerateImportFileConfig(c)) { - result = false; - } - } - } - - this->GenerateMissingTargetsCheckCode(os); - - return result; } -void cmExportInstallFileGenerator::GenerateImportPrefix(std::ostream& os) +std::string cmExportInstallFileGenerator::GetImportXcFrameworkLocation( + std::string const& config, cmTargetExport const* targetExport) const { - // Set an _IMPORT_PREFIX variable for import location properties - // to reference if they are relative to the install prefix. - std::string installPrefix = - this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition( - "CMAKE_INSTALL_PREFIX"); - std::string const& expDest = this->IEGen->GetDestination(); - if (cmSystemTools::FileIsFullPath(expDest)) { - // The export file is being installed to an absolute path so the - // package is not relocatable. Use the configured install prefix. - /* clang-format off */ - os << - "# The installation prefix configured by this project.\n" - "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n" - "\n"; - /* clang-format on */ - } else { - // Add code to compute the installation prefix relative to the - // import file location. - std::string absDest = installPrefix + "/" + expDest; - std::string absDestS = absDest + "/"; - os << "# Compute the installation prefix relative to this file.\n" - << "get_filename_component(_IMPORT_PREFIX" - << " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"; - if (cmHasLiteralPrefix(absDestS, "/lib/") || - cmHasLiteralPrefix(absDestS, "/lib64/") || - cmHasLiteralPrefix(absDestS, "/libx32/") || - cmHasLiteralPrefix(absDestS, "/usr/lib/") || - cmHasLiteralPrefix(absDestS, "/usr/lib64/") || - cmHasLiteralPrefix(absDestS, "/usr/libx32/")) { - // Handle "/usr move" symlinks created by some Linux distros. - /* clang-format off */ - os << - "# Use original install prefix when loaded through a\n" - "# cross-prefix symbolic link such as /lib -> /usr/lib.\n" - "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n" - "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n" - "if(_realCurr STREQUAL _realOrig)\n" - " set(_IMPORT_PREFIX \"" << absDest << "\")\n" - "endif()\n" - "unset(_realOrig)\n" - "unset(_realCurr)\n"; - /* clang-format on */ - } - std::string dest = expDest; - while (!dest.empty()) { - os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" " - "PATH)\n"; - dest = cmSystemTools::GetFilenamePath(dest); + std::string importedXcFrameworkLocation = targetExport->XcFrameworkLocation; + if (!importedXcFrameworkLocation.empty()) { + importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( + importedXcFrameworkLocation, + cmGeneratorExpression::PreprocessContext::InstallInterface, true); + importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( + importedXcFrameworkLocation, targetExport->Target->GetLocalGenerator(), + config, targetExport->Target, nullptr, targetExport->Target); + if (!importedXcFrameworkLocation.empty() && + !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) && + !cmHasPrefix(importedXcFrameworkLocation, + this->GetImportPrefixWithSlash())) { + return cmStrCat(this->GetImportPrefixWithSlash(), + importedXcFrameworkLocation); } - os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n" - << " set(_IMPORT_PREFIX \"\")\n" - << "endif()\n" - << "\n"; } -} -void cmExportInstallFileGenerator::CleanupTemporaryVariables(std::ostream& os) -{ - /* clang-format off */ - os << "# Cleanup temporary variables.\n" - << "set(_IMPORT_PREFIX)\n" - << "\n"; - /* clang-format on */ -} - -void cmExportInstallFileGenerator::LoadConfigFiles(std::ostream& os) -{ - // Now load per-configuration properties for them. - /* clang-format off */ - os << "# Load information for each installed configuration.\n" - << "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/" - << this->GetConfigImportFileGlob() << "\")\n" - << "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n" - << " include(\"${_cmake_config_file}\")\n" - << "endforeach()\n" - << "unset(_cmake_config_file)\n" - << "unset(_cmake_config_files)\n" - << "\n"; - /* clang-format on */ -} - -void cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string& input) -{ - cmGeneratorExpression::ReplaceInstallPrefix(input, "${_IMPORT_PREFIX}"); -} - -bool cmExportInstallFileGenerator::GenerateImportFileConfig( - const std::string& config) -{ - // Skip configurations not enabled for this export. - if (!this->IEGen->InstallsForConfig(config)) { - return true; - } - - // Construct the name of the file to generate. - std::string fileName = cmStrCat(this->FileDir, '/', this->FileBase, '-'); - if (!config.empty()) { - fileName += cmSystemTools::LowerCase(config); - } else { - fileName += "noconfig"; - } - fileName += this->FileExt; - - // Open the output file to generate it. - cmGeneratedFileStream exportFileStream(fileName, true); - if (!exportFileStream) { - std::string se = cmSystemTools::GetLastSystemError(); - std::ostringstream e; - e << "cannot write to file \"" << fileName << "\": " << se; - cmSystemTools::Error(e.str()); - return false; - } - exportFileStream.SetCopyIfDifferent(true); - std::ostream& os = exportFileStream; - - // Start with the import file header. - this->GenerateImportHeaderCode(os, config); - - // Generate the per-config target information. - this->GenerateImportConfig(os, config); - - // End with the import file footer. - this->GenerateImportFooterCode(os); - - // Record this per-config import file. - this->ConfigImportFiles[config] = fileName; - - return true; -} - -void cmExportInstallFileGenerator::GenerateImportTargetsConfig( - std::ostream& os, const std::string& config, std::string const& suffix) -{ - // Add each target in the set to the export. - for (std::unique_ptr<cmTargetExport> const& te : - this->IEGen->GetExportSet()->GetTargetExports()) { - // Collect import properties for this target. - if (this->GetExportTargetType(te.get()) == - cmStateEnums::INTERFACE_LIBRARY) { - continue; - } - - ImportPropertyMap properties; - std::set<std::string> importedLocations; - - this->SetImportLocationProperty(config, suffix, te->ArchiveGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->LibraryGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->RuntimeGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->ObjectsGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->FrameworkGenerator, - properties, importedLocations); - this->SetImportLocationProperty(config, suffix, te->BundleGenerator, - properties, importedLocations); - - // If any file location was set for the target add it to the - // import file. - if (!properties.empty()) { - // Get the rest of the target details. - cmGeneratorTarget* gtgt = te->Target; - this->SetImportDetailProperties(config, suffix, gtgt, properties); - - this->SetImportLinkInterface(config, suffix, - cmGeneratorExpression::InstallInterface, - gtgt, properties); - - // TODO: PUBLIC_HEADER_LOCATION - // This should wait until the build feature propagation stuff - // is done. Then this can be a propagated include directory. - // this->GenerateImportProperty(config, te->HeaderGenerator, - // properties); - - // Generate code in the export file. - std::string importedXcFrameworkLocation = te->XcFrameworkLocation; - if (!importedXcFrameworkLocation.empty()) { - importedXcFrameworkLocation = cmGeneratorExpression::Preprocess( - importedXcFrameworkLocation, - cmGeneratorExpression::PreprocessContext::InstallInterface, true); - importedXcFrameworkLocation = cmGeneratorExpression::Evaluate( - importedXcFrameworkLocation, te->Target->GetLocalGenerator(), config, - te->Target, nullptr, te->Target); - if (!importedXcFrameworkLocation.empty() && - !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) && - !cmHasLiteralPrefix(importedXcFrameworkLocation, - "${_IMPORT_PREFIX}/")) { - importedXcFrameworkLocation = - cmStrCat("${_IMPORT_PREFIX}/", importedXcFrameworkLocation); - } - } - this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties, - importedXcFrameworkLocation); - this->GenerateImportedFileChecksCode( - os, gtgt, properties, importedLocations, importedXcFrameworkLocation); - } - } + return importedXcFrameworkLocation; } void cmExportInstallFileGenerator::SetImportLocationProperty( - const std::string& config, std::string const& suffix, + std::string const& config, std::string const& suffix, cmInstallTargetGenerator* itgen, ImportPropertyMap& properties, std::set<std::string>& importedLocations) { @@ -501,11 +195,16 @@ cmStateEnums::TargetType cmExportInstallFileGenerator::GetExportTargetType( return targetType; } +std::string const& cmExportInstallFileGenerator::GetExportName() const +{ + return this->GetExportSet()->GetName(); +} + void cmExportInstallFileGenerator::HandleMissingTarget( std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) { - const std::string name = dependee->GetName(); + std::string const& name = dependee->GetName(); cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator(); auto exportInfo = this->FindNamespaces(gg, name); std::vector<std::string> const& exportFiles = exportInfo.first; @@ -524,14 +223,14 @@ void cmExportInstallFileGenerator::HandleMissingTarget( std::pair<std::vector<std::string>, std::string> cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg, - const std::string& name) + std::string const& name) const { std::vector<std::string> exportFiles; std::string ns; - const cmExportSetMap& exportSets = gg->GetExportSets(); + cmExportSetMap const& exportSets = gg->GetExportSets(); for (auto const& expIt : exportSets) { - const cmExportSet& exportSet = expIt.second; + cmExportSet const& exportSet = expIt.second; bool containsTarget = false; for (auto const& target : exportSet.GetTargetExports()) { @@ -556,11 +255,11 @@ cmExportInstallFileGenerator::FindNamespaces(cmGlobalGenerator* gg, void cmExportInstallFileGenerator::ComplainAboutMissingTarget( cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, - std::vector<std::string> const& exportFiles) + std::vector<std::string> const& exportFiles) const { std::ostringstream e; - e << "install(EXPORT \"" << this->IEGen->GetExportSet()->GetName() - << "\" ...) " + e << "install(" << this->IEGen->InstallSubcommand() << " \"" + << this->GetExportName() << "\" ...) " << "includes target \"" << depender->GetName() << "\" which requires target \"" << dependee->GetName() << "\" "; if (exportFiles.empty()) { @@ -573,220 +272,371 @@ void cmExportInstallFileGenerator::ComplainAboutMissingTarget( "\"" << dependee->GetName() << "\" target to a single export."; } - cmSystemTools::Error(e.str()); + this->ReportError(e.str()); +} + +void cmExportInstallFileGenerator::ComplainAboutDuplicateTarget( + std::string const& targetName) const +{ + std::ostringstream e; + e << "install(" << this->IEGen->InstallSubcommand() << " \"" + << this->GetExportName() << "\" ...) " + << "includes target \"" << targetName + << "\" more than once in the export set."; + this->ReportError(e.str()); +} + +void cmExportInstallFileGenerator::ReportError( + std::string const& errorMessage) const +{ + cmSystemTools::Error(errorMessage); } std::string cmExportInstallFileGenerator::InstallNameDir( - cmGeneratorTarget const* target, const std::string& config) + cmGeneratorTarget const* target, std::string const& config) { std::string install_name_dir; cmMakefile* mf = target->Target->GetMakefile(); if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { - install_name_dir = - target->GetInstallNameDirForInstallTree(config, "${_IMPORT_PREFIX}"); + auto const& prefix = this->GetInstallPrefix(); + install_name_dir = target->GetInstallNameDirForInstallTree(config, prefix); } return install_name_dir; } -namespace { -bool EntryIsContextSensitive( - const std::unique_ptr<cmCompiledGeneratorExpression>& cge) +std::string cmExportInstallFileGenerator::GetCxxModuleFile() const { - return cge->GetHadContextSensitiveCondition(); -} + return this->GetCxxModuleFile(this->GetExportSet()->GetName()); } -std::string cmExportInstallFileGenerator::GetFileSetDirectories( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) +bool cmExportInstallFileGenerator::CollectExports( + std::function<void(cmTargetExport const*)> const& visitor) { - std::vector<std::string> resultVector; - - auto configs = - gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - - cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); - auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination()); - - for (auto const& config : configs) { - auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte); - auto dest = cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { - dest = cmStrCat("${_IMPORT_PREFIX}/", dest); + auto pred = [&](std::unique_ptr<cmTargetExport> const& te) -> bool { + if (te->NamelinkOnly) { + return true; } - - auto const& type = fileSet->GetType(); - // C++ modules do not support interface file sets which are dependent upon - // the configuration. - if (cge->GetHadContextSensitiveCondition() && type == "CXX_MODULES"_s) { - auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive base file entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return std::string{}; + if (this->ExportedTargets.insert(te->Target).second) { + visitor(te.get()); + return true; } - if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) { - resultVector.push_back( - cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); - } else { - resultVector.emplace_back(cmStrCat('"', dest, '"')); - break; - } - } + this->ComplainAboutDuplicateTarget(te->Target->GetName()); + return false; + }; - return cmJoin(resultVector, " "); + auto const& targets = this->GetExportSet()->GetTargetExports(); + return std::all_of(targets.begin(), targets.end(), pred); } -std::string cmExportInstallFileGenerator::GetFileSetFiles( - cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) +bool cmExportInstallFileGenerator::PopulateInterfaceProperties( + cmTargetExport const* targetExport, ImportPropertyMap& properties) { - std::vector<std::string> resultVector; + cmGeneratorTarget const* const gt = targetExport->Target; + + std::string includesDestinationDirs; + this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt, + cmGeneratorExpression::InstallInterface, + properties); + this->PopulateIncludeDirectoriesInterface( + gt, cmGeneratorExpression::InstallInterface, properties, *targetExport, + includesDestinationDirs); + this->PopulateLinkDirectoriesInterface( + gt, cmGeneratorExpression::InstallInterface, properties); + this->PopulateLinkDependsInterface( + gt, cmGeneratorExpression::InstallInterface, properties); + this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface, + properties); + + return this->PopulateInterfaceProperties( + gt, includesDestinationDirs, cmGeneratorExpression::InstallInterface, + properties); +} - auto configs = - gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); +namespace { +bool isSubDirectory(std::string const& a, std::string const& b) +{ + return (cmSystemTools::ComparePath(a, b) || + cmSystemTools::IsSubDirectory(a, b)); +} + +bool checkInterfaceDirs(std::string const& prepro, + cmGeneratorTarget const* target, + std::string const& prop) +{ + std::string const& installDir = + target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); + std::string const& topSourceDir = + target->GetLocalGenerator()->GetSourceDirectory(); + std::string const& topBinaryDir = + target->GetLocalGenerator()->GetBinaryDirectory(); - auto fileEntries = fileSet->CompileFileEntries(); - auto directoryEntries = fileSet->CompileDirectoryEntries(); + std::vector<std::string> parts; + cmGeneratorExpression::Split(prepro, parts); - cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance()); - auto destCge = - destGe.Parse(te->FileSetGenerators.at(fileSet)->GetDestination()); + bool const inSourceBuild = topSourceDir == topBinaryDir; - for (auto const& config : configs) { - auto directories = fileSet->EvaluateDirectoryEntries( - directoryEntries, gte->LocalGenerator, config, gte); + bool hadFatalError = false; - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - fileSet->EvaluateFileEntry(directories, files, entry, - gte->LocalGenerator, config, gte); + for (std::string const& li : parts) { + size_t genexPos = cmGeneratorExpression::Find(li); + if (genexPos == 0) { + continue; } - auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte); - auto dest = - cmStrCat(cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap), - '/'); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { - dest = cmStrCat("${_IMPORT_PREFIX}/", dest); + if (cmHasLiteralPrefix(li, "${_IMPORT_PREFIX}")) { + continue; } - - bool const contextSensitive = destCge->GetHadContextSensitiveCondition() || - std::any_of(directoryEntries.begin(), directoryEntries.end(), - EntryIsContextSensitive) || - std::any_of(fileEntries.begin(), fileEntries.end(), - EntryIsContextSensitive); - - auto const& type = fileSet->GetType(); - // C++ modules do not support interface file sets which are dependent upon - // the configuration. - if (contextSensitive && type == "CXX_MODULES"_s) { - auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); - std::ostringstream e; - e << "The \"" << gte->GetName() << "\" target's interface file set \"" - << fileSet->GetName() << "\" of type \"" << type - << "\" contains context-sensitive base file entries which is not " - "supported."; - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return std::string{}; + MessageType messageType = MessageType::FATAL_ERROR; + std::ostringstream e; + if (genexPos != std::string::npos) { + if (prop == "INTERFACE_INCLUDE_DIRECTORIES") { + switch (target->GetPolicyStatusCMP0041()) { + case cmPolicies::WARN: + messageType = MessageType::WARNING; + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0041) << "\n"; + break; + case cmPolicies::OLD: + continue; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + hadFatalError = true; + break; // Issue fatal message. + } + } else { + hadFatalError = true; + } } - - for (auto const& it : files) { - auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/'); - for (auto const& filename : it.second) { - auto relFile = - cmStrCat(prefix, cmSystemTools::GetFilenameName(filename)); - auto escapedFile = - cmStrCat(dest, - cmOutputConverter::EscapeForCMake( - relFile, cmOutputConverter::WrapQuotes::NoWrap)); - if (contextSensitive && configs.size() != 1) { - resultVector.push_back( - cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\"")); - } else { - resultVector.emplace_back(cmStrCat('"', escapedFile, '"')); + if (!cmSystemTools::FileIsFullPath(li)) { + /* clang-format off */ + e << "Target \"" << target->GetName() << "\" " << prop << + " property contains relative path:\n" + " \"" << li << "\""; + /* clang-format on */ + target->GetLocalGenerator()->IssueMessage(messageType, e.str()); + } + bool inBinary = isSubDirectory(li, topBinaryDir); + bool inSource = isSubDirectory(li, topSourceDir); + if (isSubDirectory(li, installDir)) { + // The include directory is inside the install tree. If the + // install tree is not inside the source tree or build tree then + // fall through to the checks below that the include directory is not + // also inside the source tree or build tree. + bool shouldContinue = + (!inBinary || isSubDirectory(installDir, topBinaryDir)) && + (!inSource || isSubDirectory(installDir, topSourceDir)); + + if (prop == "INTERFACE_INCLUDE_DIRECTORIES") { + if (!shouldContinue) { + switch (target->GetPolicyStatusCMP0052()) { + case cmPolicies::WARN: { + std::ostringstream s; + s << cmPolicies::GetPolicyWarning(cmPolicies::CMP0052) << "\n"; + s << "Directory:\n \"" << li + << "\"\nin " + "INTERFACE_INCLUDE_DIRECTORIES of target \"" + << target->GetName() + << "\" is a subdirectory of the install " + "directory:\n \"" + << installDir + << "\"\nhowever it is also " + "a subdirectory of the " + << (inBinary ? "build" : "source") << " tree:\n \"" + << (inBinary ? topBinaryDir : topSourceDir) << "\"\n"; + target->GetLocalGenerator()->IssueMessage( + MessageType::AUTHOR_WARNING, s.str()); + CM_FALLTHROUGH; + } + case cmPolicies::OLD: + shouldContinue = true; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::NEW: + break; + } } } + if (shouldContinue) { + continue; + } } - - if (!(contextSensitive && configs.size() != 1)) { - break; + if (inBinary) { + /* clang-format off */ + e << "Target \"" << target->GetName() << "\" " << prop << + " property contains path:\n" + " \"" << li << "\"\nwhich is prefixed in the build directory."; + /* clang-format on */ + target->GetLocalGenerator()->IssueMessage(messageType, e.str()); + } + if (!inSourceBuild) { + if (inSource) { + e << "Target \"" << target->GetName() << "\" " << prop + << " property contains path:\n" + " \"" + << li << "\"\nwhich is prefixed in the source directory."; + target->GetLocalGenerator()->IssueMessage(messageType, e.str()); + } } } - - return cmJoin(resultVector, " "); + return !hadFatalError; +} } -std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const +void cmExportInstallFileGenerator::PopulateSourcesInterface( + cmGeneratorTarget const* gt, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) { - return IEGen->GetCxxModuleDirectory(); + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + char const* const propName = "INTERFACE_SOURCES"; + cmValue input = gt->GetProperty(propName); + + if (!input) { + return; + } + + if (input->empty()) { + properties[propName].clear(); + return; + } + + std::string prepro = + cmGeneratorExpression::Preprocess(*input, preprocessRule, true); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, gt); + + if (!checkInterfaceDirs(prepro, gt, propName)) { + return; + } + properties[propName] = prepro; + } } -void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation( - std::string const& name, std::ostream& os) const +void cmExportInstallFileGenerator::PopulateIncludeDirectoriesInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties, cmTargetExport const& te, + std::string& includesDestinationDirs) { - // Now load per-configuration properties for them. - /* clang-format off */ - os << "# Load information for each installed configuration.\n" - "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << "-*.cmake\")\n" - "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n" - " include(\"${_cmake_cxx_module_include}\")\n" - "endforeach()\n" - "unset(_cmake_cxx_module_include)\n" - "unset(_cmake_cxx_module_includes)\n"; - /* clang-format on */ + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + includesDestinationDirs.clear(); + + char const* const propName = "INTERFACE_INCLUDE_DIRECTORIES"; + cmValue input = target->GetProperty(propName); + + cmGeneratorExpression ge(*target->Makefile->GetCMakeInstance()); + + std::string dirs = cmGeneratorExpression::Preprocess( + cmList::to_string(target->Target->GetInstallIncludeDirectoriesEntries(te)), + preprocessRule, true); + this->ReplaceInstallPrefix(dirs); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(dirs); + std::string exportDirs = + cge->Evaluate(target->GetLocalGenerator(), "", target); + + if (cge->GetHadContextSensitiveCondition()) { + cmLocalGenerator* lg = target->GetLocalGenerator(); + std::ostringstream e; + e << "Target \"" << target->GetName() + << "\" is installed with " + "INCLUDES DESTINATION set to a context sensitive path. Paths which " + "depend on the configuration, policy values or the link interface " + "are " + "not supported. Consider using target_include_directories instead."; + lg->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return; + } + + if (!input && exportDirs.empty()) { + return; + } + if ((input && input->empty()) && exportDirs.empty()) { + // Set to empty + properties[propName].clear(); + return; + } + + this->AddImportPrefix(exportDirs); + includesDestinationDirs = exportDirs; + + std::string includes = (input ? *input : ""); + char const* const sep = input ? ";" : ""; + includes += sep + exportDirs; + std::string prepro = + cmGeneratorExpression::Preprocess(includes, preprocessRule, true); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, target); + + if (!checkInterfaceDirs(prepro, target, propName)) { + return; + } + properties[propName] = prepro; + } } -bool cmExportInstallFileGenerator:: - GenerateImportCxxModuleConfigTargetInclusion(std::string const& name, - std::string const& config) +void cmExportInstallFileGenerator::PopulateLinkDependsInterface( + cmGeneratorTarget const* gt, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) { - auto cxx_modules_dirname = this->GetCxxModulesDirectory(); - if (cxx_modules_dirname.empty()) { - return true; + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + char const* const propName = "INTERFACE_LINK_DEPENDS"; + cmValue input = gt->GetProperty(propName); + + if (!input) { + return; } - std::string filename_config = config; - if (filename_config.empty()) { - filename_config = "noconfig"; + if (input->empty()) { + properties[propName].clear(); + return; } - std::string const dest = - cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/'); - std::string fileName = - cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake"); + std::string prepro = + cmGeneratorExpression::Preprocess(*input, preprocessRule, true); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, gt); - cmGeneratedFileStream os(fileName, true); - if (!os) { - std::string se = cmSystemTools::GetLastSystemError(); - std::ostringstream e; - e << "cannot write to file \"" << fileName << "\": " << se; - cmSystemTools::Error(e.str()); - return false; + if (!checkInterfaceDirs(prepro, gt, propName)) { + return; + } + properties[propName] = prepro; } - os.SetCopyIfDifferent(true); +} - // Record this per-config import file. - this->ConfigCxxModuleFiles[config] = fileName; +void cmExportInstallFileGenerator::PopulateLinkDirectoriesInterface( + cmGeneratorTarget const* gt, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties) +{ + assert(preprocessRule == cmGeneratorExpression::InstallInterface); - auto& prop_files = this->ConfigCxxModuleTargetFiles[config]; - for (auto const* tgt : this->ExportedTargets) { - // Only targets with C++ module sources will have a - // collator-generated install script. - if (!tgt->HaveCxx20ModuleSources()) { - continue; - } + char const* const propName = "INTERFACE_LINK_DIRECTORIES"; + cmValue input = gt->GetProperty(propName); + + if (!input) { + return; + } - auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(), - '-', filename_config, ".cmake"); - prop_files.emplace_back(cmStrCat(dest, prop_filename)); - os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n"; + if (input->empty()) { + properties[propName].clear(); + return; } - return true; + std::string prepro = + cmGeneratorExpression::Preprocess(*input, preprocessRule, true); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, gt); + + if (!checkInterfaceDirs(prepro, gt, propName)) { + return; + } + properties[propName] = prepro; + } } diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index 7a72584..24cda9d 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -4,19 +4,21 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include <iosfwd> +#include <functional> #include <map> #include <set> #include <string> #include <utility> #include <vector> +#include <cm/string_view> + #include "cmExportFileGenerator.h" +#include "cmGeneratorExpression.h" #include "cmInstallExportGenerator.h" #include "cmStateTypes.h" class cmExportSet; -class cmFileSet; class cmGeneratorTarget; class cmGlobalGenerator; class cmInstallTargetGenerator; @@ -25,18 +27,10 @@ class cmTargetExport; /** \class cmExportInstallFileGenerator * \brief Generate a file exporting targets from an install tree. * - * cmExportInstallFileGenerator generates files exporting targets from - * install an installation tree. The files are placed in a temporary - * location for installation by cmInstallExportGenerator. One main - * file is generated that creates the imported targets and loads - * per-configuration files. Target locations and settings for each - * configuration are written to these per-configuration files. After - * installation the main file loads the configurations that have been - * installed. - * - * This is used to implement the INSTALL(EXPORT) command. + * cmExportInstallFileGenerator is the generic interface class for generating + * export files for an install tree. */ -class cmExportInstallFileGenerator : public cmExportFileGenerator +class cmExportInstallFileGenerator : virtual public cmExportFileGenerator { public: /** Construct with the export installer that will install the @@ -51,6 +45,9 @@ public: return this->ConfigImportFiles; } + /** Get the temporary location of the config-agnostic C++ module file. */ + std::string GetCxxModuleFile() const; + /** Get the per-config C++ module file generated for each configuration. This maps from the configuration name to the file temporary location for installation. */ @@ -70,65 +67,73 @@ public: /** Compute the globbing expression used to load per-config import files from the main file. */ - std::string GetConfigImportFileGlob(); + virtual std::string GetConfigImportFileGlob() const = 0; protected: - // Implement virtual methods from the superclass. - bool GenerateMainFile(std::ostream& os) override; - void GenerateImportTargetsConfig(std::ostream& os, const std::string& config, - std::string const& suffix) override; cmStateEnums::TargetType GetExportTargetType( cmTargetExport const* targetExport) const; + + virtual std::string const& GetExportName() const; + + std::string GetInstallPrefix() const + { + cm::string_view const& prefixWithSlash = this->GetImportPrefixWithSlash(); + return std::string(prefixWithSlash.data(), prefixWithSlash.length() - 1); + } + void HandleMissingTarget(std::string& link_libs, cmGeneratorTarget const* depender, cmGeneratorTarget* dependee) override; - void ReplaceInstallPrefix(std::string& input) override; + void ReplaceInstallPrefix(std::string& input) const override; - void ComplainAboutMissingTarget(cmGeneratorTarget const* depender, - cmGeneratorTarget const* dependee, - std::vector<std::string> const& exportFiles); + void ComplainAboutMissingTarget( + cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, + std::vector<std::string> const& exportFiles) const; - std::pair<std::vector<std::string>, std::string> FindNamespaces( - cmGlobalGenerator* gg, const std::string& name); - - /** Generate the relative import prefix. */ - virtual void GenerateImportPrefix(std::ostream&); + void ComplainAboutDuplicateTarget( + std::string const& targetName) const override; - /** Generate the relative import prefix. */ - virtual void LoadConfigFiles(std::ostream&); - - virtual void CleanupTemporaryVariables(std::ostream&); + std::pair<std::vector<std::string>, std::string> FindNamespaces( + cmGlobalGenerator* gg, std::string const& name) const; - /** Generate a per-configuration file for the targets. */ - virtual bool GenerateImportFileConfig(const std::string& config); + void ReportError(std::string const& errorMessage) const override; /** Fill in properties indicating installed file locations. */ - void SetImportLocationProperty(const std::string& config, + void SetImportLocationProperty(std::string const& config, std::string const& suffix, cmInstallTargetGenerator* itgen, ImportPropertyMap& properties, std::set<std::string>& importedLocations); std::string InstallNameDir(cmGeneratorTarget const* target, - const std::string& config) override; + std::string const& config) override; - std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, - cmTargetExport* te) override; - std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, - cmTargetExport* te) override; + using cmExportFileGenerator::GetCxxModuleFile; - std::string GetCxxModulesDirectory() const override; - void GenerateCxxModuleConfigInformation(std::string const&, - std::ostream&) const override; - bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&, - std::string const&); + /** Walk the list of targets to be exported. Returns true iff no duplicates + are found. */ + bool CollectExports( + std::function<void(cmTargetExport const*)> const& visitor); cmExportSet* GetExportSet() const override { return this->IEGen->GetExportSet(); } + std::string GetImportXcFrameworkLocation( + std::string const& config, cmTargetExport const* targetExport) const; + + using cmExportFileGenerator::PopulateInterfaceProperties; + bool PopulateInterfaceProperties(cmTargetExport const* targetExport, + ImportPropertyMap& properties); + + void PopulateImportProperties(std::string const& config, + std::string const& suffix, + cmTargetExport const* targetExport, + ImportPropertyMap& properties, + std::set<std::string>& importedLocations); + cmInstallExportGenerator* IEGen; // The import file generated for each configuration. @@ -137,4 +142,29 @@ protected: std::map<std::string, std::string> ConfigCxxModuleFiles; // The C++ module property target files generated for each configuration. std::map<std::string, std::vector<std::string>> ConfigCxxModuleTargetFiles; + +private: + void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target, + ImportPropertyMap& properties); + void PopulateCustomTransitiveInterfaceProperties( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + void PopulateIncludeDirectoriesInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties, cmTargetExport const& te, + std::string& includesDestinationDirs); + void PopulateSourcesInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + void PopulateLinkDirectoriesInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); + void PopulateLinkDependsInterface( + cmGeneratorTarget const* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties); }; diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx index 7ce5cd9..021e071 100644 --- a/Source/cmExportTryCompileFileGenerator.cxx +++ b/Source/cmExportTryCompileFileGenerator.cxx @@ -19,19 +19,26 @@ #include "cmOutputConverter.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSystemTools.h" #include "cmTarget.h" #include "cmValue.h" class cmTargetExport; cmExportTryCompileFileGenerator::cmExportTryCompileFileGenerator( - cmGlobalGenerator* gg, const std::vector<std::string>& targets, + cmGlobalGenerator* gg, std::vector<std::string> const& targets, cmMakefile* mf, std::set<std::string> const& langs) : Languages(langs.begin(), langs.end()) { gg->CreateImportedGenerationObjects(mf, targets, this->Exports); } +void cmExportTryCompileFileGenerator::ReportError( + std::string const& errorMessage) const +{ + cmSystemTools::Error(errorMessage); +} + bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os) { std::set<cmGeneratorTarget const*> emitted; @@ -61,7 +68,7 @@ bool cmExportTryCompileFileGenerator::GenerateMainFile(std::ostream& os) } std::string cmExportTryCompileFileGenerator::FindTargets( - const std::string& propName, cmGeneratorTarget const* tgt, + std::string const& propName, cmGeneratorTarget const* tgt, std::string const& language, std::set<cmGeneratorTarget const*>& emitted) { cmValue prop = tgt->GetProperty(propName); @@ -94,7 +101,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets( std::string result = cge->Evaluate(tgt->GetLocalGenerator(), this->Config, &gDummyHead, &dagChecker, tgt, language); - const std::set<cmGeneratorTarget const*>& allTargets = + std::set<cmGeneratorTarget const*> const& allTargets = cge->GetAllTargetsSeen(); for (cmGeneratorTarget const* target : allTargets) { if (emitted.insert(target).second) { @@ -105,7 +112,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets( } void cmExportTryCompileFileGenerator::PopulateProperties( - const cmGeneratorTarget* target, ImportPropertyMap& properties, + cmGeneratorTarget const* target, ImportPropertyMap& properties, std::set<cmGeneratorTarget const*>& emitted) { // Look through all non-special properties. @@ -140,7 +147,7 @@ void cmExportTryCompileFileGenerator::PopulateProperties( } std::string cmExportTryCompileFileGenerator::InstallNameDir( - cmGeneratorTarget const* target, const std::string& config) + cmGeneratorTarget const* target, std::string const& config) { std::string install_name_dir; @@ -153,14 +160,14 @@ std::string cmExportTryCompileFileGenerator::InstallNameDir( } std::string cmExportTryCompileFileGenerator::GetFileSetDirectories( - cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport* /*te*/) + cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport const* /*te*/) { return cmOutputConverter::EscapeForCMake( cmList::to_string(fileSet->GetDirectoryEntries())); } std::string cmExportTryCompileFileGenerator::GetFileSetFiles( - cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport* /*te*/) + cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport const* /*te*/) { return cmOutputConverter::EscapeForCMake( cmList::to_string(fileSet->GetFileEntries())); diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h index 4c7d287..4b290e6 100644 --- a/Source/cmExportTryCompileFileGenerator.h +++ b/Source/cmExportTryCompileFileGenerator.h @@ -9,7 +9,7 @@ #include <string> #include <vector> -#include "cmExportFileGenerator.h" +#include "cmExportCMakeConfigGenerator.h" class cmFileSet; class cmGeneratorTarget; @@ -17,7 +17,7 @@ class cmGlobalGenerator; class cmMakefile; class cmTargetExport; -class cmExportTryCompileFileGenerator : public cmExportFileGenerator +class cmExportTryCompileFileGenerator : public cmExportCMakeConfigGenerator { public: cmExportTryCompileFileGenerator(cmGlobalGenerator* gg, @@ -26,13 +26,17 @@ public: std::set<std::string> const& langs); /** Set the list of targets to export. */ - void SetConfig(const std::string& config) { this->Config = config; } + void SetConfig(std::string const& config) { this->Config = config; } protected: // Implement virtual methods from the superclass. + void ComplainAboutDuplicateTarget( + std::string const& /*targetName*/) const override{}; + void ReportError(std::string const& errorMessage) const override; + bool GenerateMainFile(std::ostream& os) override; - void GenerateImportTargetsConfig(std::ostream&, const std::string&, + void GenerateImportTargetsConfig(std::ostream&, std::string const&, std::string const&) override { } @@ -43,17 +47,17 @@ protected: void PopulateProperties(cmGeneratorTarget const* target, ImportPropertyMap& properties, - std::set<const cmGeneratorTarget*>& emitted); + std::set<cmGeneratorTarget const*>& emitted); std::string InstallNameDir(cmGeneratorTarget const* target, - const std::string& config) override; + std::string const& config) override; std::string GetFileSetDirectories(cmGeneratorTarget* target, cmFileSet* fileSet, - cmTargetExport* te) override; + cmTargetExport const* te) override; std::string GetFileSetFiles(cmGeneratorTarget* target, cmFileSet* fileSet, - cmTargetExport* te) override; + cmTargetExport const* te) override; std::string GetCxxModulesDirectory() const override { return {}; } void GenerateCxxModuleConfigInformation(std::string const&, @@ -62,10 +66,10 @@ protected: } private: - std::string FindTargets(const std::string& prop, - const cmGeneratorTarget* tgt, + std::string FindTargets(std::string const& prop, + cmGeneratorTarget const* tgt, std::string const& language, - std::set<const cmGeneratorTarget*>& emitted); + std::set<cmGeneratorTarget const*>& emitted); std::vector<cmGeneratorTarget const*> Exports; std::string Config; diff --git a/Source/cmInstallAndroidMKExportGenerator.cxx b/Source/cmInstallAndroidMKExportGenerator.cxx new file mode 100644 index 0000000..b038ac1 --- /dev/null +++ b/Source/cmInstallAndroidMKExportGenerator.cxx @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallAndroidMKExportGenerator.h" + +#include <utility> + +#include <cm/memory> + +#include "cmExportInstallAndroidMKGenerator.h" +#include "cmExportInstallFileGenerator.h" +#include "cmListFileCache.h" + +class cmExportSet; + +cmInstallAndroidMKExportGenerator::cmInstallAndroidMKExportGenerator( + cmExportSet* exportSet, std::string destination, std::string filePermissions, + std::vector<std::string> const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, cmListFileBacktrace backtrace) + : cmInstallExportGenerator(exportSet, std::move(destination), + std::move(filePermissions), configurations, + std::move(component), message, excludeFromAll, + std::move(filename), std::move(targetNamespace), + std::string{}, std::move(backtrace)) +{ + this->EFGen = cm::make_unique<cmExportInstallAndroidMKGenerator>(this); +} + +cmInstallAndroidMKExportGenerator::~cmInstallAndroidMKExportGenerator() = + default; diff --git a/Source/cmInstallAndroidMKExportGenerator.h b/Source/cmInstallAndroidMKExportGenerator.h new file mode 100644 index 0000000..4ee80d4 --- /dev/null +++ b/Source/cmInstallAndroidMKExportGenerator.h @@ -0,0 +1,36 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> +#include <vector> + +#include "cmInstallExportGenerator.h" + +class cmExportSet; +class cmListFileBacktrace; + +/** \class cmInstallAndroidMKExportGenerator + * \brief Generate rules for creating Android .mk export files. + */ +class cmInstallAndroidMKExportGenerator : public cmInstallExportGenerator +{ +public: + cmInstallAndroidMKExportGenerator( + cmExportSet* exportSet, std::string destination, + std::string filePermissions, + std::vector<std::string> const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, cmListFileBacktrace backtrace); + cmInstallAndroidMKExportGenerator(cmInstallAndroidMKExportGenerator const&) = + delete; + ~cmInstallAndroidMKExportGenerator() override; + + cmInstallAndroidMKExportGenerator& operator=( + cmInstallAndroidMKExportGenerator const&) = delete; + + char const* InstallSubcommand() const override + { + return "EXPORT_ANDROID_MK"; + } +}; diff --git a/Source/cmInstallCMakeConfigExportGenerator.cxx b/Source/cmInstallCMakeConfigExportGenerator.cxx new file mode 100644 index 0000000..e247e5b --- /dev/null +++ b/Source/cmInstallCMakeConfigExportGenerator.cxx @@ -0,0 +1,43 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallCMakeConfigExportGenerator.h" + +#include <utility> + +#include <cm/memory> + +#include "cmExportInstallCMakeConfigGenerator.h" +#include "cmExportInstallFileGenerator.h" +#include "cmListFileCache.h" + +class cmExportSet; + +cmInstallCMakeConfigExportGenerator::cmInstallCMakeConfigExportGenerator( + cmExportSet* exportSet, std::string destination, std::string filePermissions, + std::vector<std::string> const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, std::string cxxModulesDirectory, bool exportOld, + bool exportPackageDependencies, cmListFileBacktrace backtrace) + : cmInstallExportGenerator( + exportSet, std::move(destination), std::move(filePermissions), + configurations, std::move(component), message, excludeFromAll, + std::move(filename), std::move(targetNamespace), + std::move(cxxModulesDirectory), std::move(backtrace)) + , ExportOld(exportOld) + , ExportPackageDependencies(exportPackageDependencies) +{ + this->EFGen = cm::make_unique<cmExportInstallCMakeConfigGenerator>(this); +} + +cmInstallCMakeConfigExportGenerator::~cmInstallCMakeConfigExportGenerator() = + default; + +void cmInstallCMakeConfigExportGenerator::GenerateScript(std::ostream& os) +{ + auto* const efgen = + static_cast<cmExportInstallCMakeConfigGenerator*>(this->EFGen.get()); + efgen->SetExportOld(this->ExportOld); + efgen->SetExportPackageDependencies(this->ExportPackageDependencies); + + this->cmInstallExportGenerator::GenerateScript(os); +} diff --git a/Source/cmInstallCMakeConfigExportGenerator.h b/Source/cmInstallCMakeConfigExportGenerator.h new file mode 100644 index 0000000..03296de --- /dev/null +++ b/Source/cmInstallCMakeConfigExportGenerator.h @@ -0,0 +1,42 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <iosfwd> +#include <string> +#include <vector> + +#include "cmInstallExportGenerator.h" + +class cmExportSet; +class cmListFileBacktrace; + +/** \class cmInstallCMakeConfigExportGenerator + * \brief Generate rules for creating CMake export files. + */ +class cmInstallCMakeConfigExportGenerator : public cmInstallExportGenerator +{ +public: + cmInstallCMakeConfigExportGenerator( + cmExportSet* exportSet, std::string destination, + std::string filePermissions, + std::vector<std::string> const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, std::string cxxModulesDirectory, + bool exportOld, bool exportPackageDependencies, + cmListFileBacktrace backtrace); + cmInstallCMakeConfigExportGenerator( + cmInstallCMakeConfigExportGenerator const&) = delete; + ~cmInstallCMakeConfigExportGenerator() override; + + cmInstallCMakeConfigExportGenerator& operator=( + cmInstallCMakeConfigExportGenerator const&) = delete; + + char const* InstallSubcommand() const override { return "EXPORT"; } + +protected: + void GenerateScript(std::ostream& os) override; + + bool const ExportOld; + bool const ExportPackageDependencies; +}; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 1567629..3160585 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -26,10 +26,11 @@ #include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" +#include "cmInstallAndroidMKExportGenerator.h" +#include "cmInstallCMakeConfigExportGenerator.h" #include "cmInstallCommandArguments.h" #include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" -#include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" #include "cmInstallFilesGenerator.h" #include "cmInstallGenerator.h" @@ -2028,10 +2029,10 @@ bool HandleExportAndroidMKMode(std::vector<std::string> const& args, // Create the export install generator. helper.Makefile->AddInstallGenerator( - cm::make_unique<cmInstallExportGenerator>( + cm::make_unique<cmInstallAndroidMKExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, false, + ica.GetExcludeFromAll(), std::move(fname), std::move(name_space), helper.Makefile->GetBacktrace())); return true; @@ -2151,11 +2152,11 @@ bool HandleExportMode(std::vector<std::string> const& args, // Create the export install generator. helper.Makefile->AddInstallGenerator( - cm::make_unique<cmInstallExportGenerator>( + cm::make_unique<cmInstallCMakeConfigExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory, - exportOld, false, exportPackageDependencies, + ica.GetExcludeFromAll(), std::move(fname), std::move(name_space), + std::move(cxx_modules_directory), exportOld, exportPackageDependencies, helper.Makefile->GetBacktrace())); return true; diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index 2f3da3e..9828bf2 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -6,12 +6,7 @@ #include <sstream> #include <utility> -#include <cm/memory> - #include "cmCryptoHash.h" -#ifndef CMAKE_BOOTSTRAP -# include "cmExportInstallAndroidMKGenerator.h" -#endif #include "cmExportInstallFileGenerator.h" #include "cmExportSet.h" #include "cmInstallType.h" @@ -22,29 +17,20 @@ #include "cmSystemTools.h" cmInstallExportGenerator::cmInstallExportGenerator( - cmExportSet* exportSet, std::string const& destination, - std::string file_permissions, std::vector<std::string> const& configurations, - std::string const& component, MessageLevel message, bool exclude_from_all, - std::string filename, std::string name_space, - std::string cxx_modules_directory, bool exportOld, bool android, - bool exportPackageDependencies, cmListFileBacktrace backtrace) - : cmInstallGenerator(destination, configurations, component, message, - exclude_from_all, false, std::move(backtrace)) + cmExportSet* exportSet, std::string destination, std::string filePermissions, + std::vector<std::string> const& configurations, std::string component, + MessageLevel message, bool excludeFromAll, std::string filename, + std::string targetNamespace, std::string cxxModulesDirectory, + cmListFileBacktrace backtrace) + : cmInstallGenerator(std::move(destination), configurations, + std::move(component), message, excludeFromAll, false, + std::move(backtrace)) , ExportSet(exportSet) - , FilePermissions(std::move(file_permissions)) + , FilePermissions(std::move(filePermissions)) , FileName(std::move(filename)) - , Namespace(std::move(name_space)) - , CxxModulesDirectory(std::move(cxx_modules_directory)) - , ExportOld(exportOld) - , ExportPackageDependencies(exportPackageDependencies) + , Namespace(std::move(targetNamespace)) + , CxxModulesDirectory(std::move(cxxModulesDirectory)) { - if (android) { -#ifndef CMAKE_BOOTSTRAP - this->EFGen = cm::make_unique<cmExportInstallAndroidMKGenerator>(this); -#endif - } else { - this->EFGen = cm::make_unique<cmExportInstallFileGenerator>(this); - } exportSet->AddInstallation(this); } @@ -92,7 +78,7 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os) // Skip empty sets. if (this->ExportSet->GetTargetExports().empty()) { std::ostringstream e; - e << "INSTALL(EXPORT) given unknown export \"" + e << "INSTALL(" << this->InstallSubcommand() << ") given unknown export \"" << this->ExportSet->GetName() << "\""; cmSystemTools::Error(e.str()); return; @@ -108,7 +94,6 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os) // Generate the import file for this export set. this->EFGen->SetExportFile(this->MainImportFile.c_str()); this->EFGen->SetNamespace(this->Namespace); - this->EFGen->SetExportOld(this->ExportOld); if (this->ConfigurationTypes->empty()) { if (!this->ConfigurationName.empty()) { this->EFGen->AddConfiguration(this->ConfigurationName); @@ -120,7 +105,6 @@ void cmInstallExportGenerator::GenerateScript(std::ostream& os) this->EFGen->AddConfiguration(c); } } - this->EFGen->SetExportPackageDependencies(this->ExportPackageDependencies); this->EFGen->GenerateImportFile(); // Perform the main install script generation. @@ -149,37 +133,37 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, // Now create a configuration-specific install rule for the C++ module import // property file of each configuration. - auto cxx_module_dest = + auto const cxxModuleDestination = cmStrCat(this->Destination, '/', this->CxxModulesDirectory); - std::string config_file_example; - for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) { - config_file_example = i.second; - break; - } - if (!config_file_example.empty()) { + auto const cxxModuleInstallFilePath = this->EFGen->GetCxxModuleFile(); + auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob(); + if (!cxxModuleInstallFilePath.empty() && !configImportFilesGlob.empty()) { + auto const cxxModuleFilename = + cmSystemTools::GetFilenameName(cxxModuleInstallFilePath); + // Remove old per-configuration export files if the main changes. - std::string installedDir = cmStrCat( - "$ENV{DESTDIR}", ConvertToAbsoluteDestination(cxx_module_dest), '/'); - std::string installedFile = cmStrCat(installedDir, "/cxx-modules-", - this->ExportSet->GetName(), ".cmake"); - std::string toInstallFile = - cmStrCat(cmSystemTools::GetFilenamePath(config_file_example), - "/cxx-modules-", this->ExportSet->GetName(), ".cmake"); + std::string installedDir = + cmStrCat("$ENV{DESTDIR}", + ConvertToAbsoluteDestination(cxxModuleDestination), '/'); + std::string installedFile = cmStrCat(installedDir, cxxModuleFilename); os << indent << "if(EXISTS \"" << installedFile << "\")\n"; Indent indentN = indent.Next(); Indent indentNN = indentN.Next(); Indent indentNNN = indentNN.Next(); - /* clang-format off */ os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n" << indentN << " \"" << installedFile << "\"\n" - << indentN << " \"" << toInstallFile << "\")\n"; + << indentN << " \"" << cxxModuleInstallFilePath << "\")\n"; os << indentN << "if(_cmake_export_file_changed)\n"; os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir - << this->EFGen->GetConfigImportFileGlob() << "\")\n"; + << configImportFilesGlob << "\")\n"; os << indentNN << "if(_cmake_old_config_files)\n"; - os << indentNNN << "string(REPLACE \";\" \", \" _cmake_old_config_files_text \"${_cmake_old_config_files}\")\n"; - os << indentNNN << R"(message(STATUS "Old C++ module export file \")" << installedFile - << "\\\" will be replaced. Removing files [${_cmake_old_config_files_text}].\")\n"; + os << indentNNN + << "string(REPLACE \";\" \", \" _cmake_old_config_files_text " + "\"${_cmake_old_config_files}\")\n"; + os << indentNNN << R"(message(STATUS "Old C++ module export file \")" + << installedFile + << "\\\" will be replaced. " + "Removing files [${_cmake_old_config_files_text}].\")\n"; os << indentNNN << "unset(_cmake_old_config_files_text)\n"; os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n"; os << indentNN << "endif()\n"; @@ -187,12 +171,11 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, os << indentN << "endif()\n"; os << indentN << "unset(_cmake_export_file_changed)\n"; os << indent << "endif()\n"; - /* clang-format on */ // All of these files are siblings; get its location to know where the // "anchor" file is. - files.push_back(toInstallFile); - this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files, + files.push_back(cxxModuleInstallFilePath); + this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files, false, this->FilePermissions.c_str(), nullptr, nullptr, nullptr, indent); files.clear(); @@ -201,7 +184,7 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, files.push_back(i.second); std::string config_test = this->CreateConfigTest(i.first); os << indent << "if(" << config_test << ")\n"; - this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files, + this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files, false, this->FilePermissions.c_str(), nullptr, nullptr, nullptr, indent.Next()); os << indent << "endif()\n"; @@ -210,9 +193,9 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) { std::string config_test = this->CreateConfigTest(i.first); os << indent << "if(" << config_test << ")\n"; - this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, i.second, - false, this->FilePermissions.c_str(), nullptr, - nullptr, nullptr, indent.Next()); + this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, + i.second, false, this->FilePermissions.c_str(), + nullptr, nullptr, nullptr, indent.Next()); os << indent << "endif()\n"; files.clear(); } @@ -221,33 +204,37 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os, Indent indent) { - // Remove old per-configuration export files if the main changes. - std::string installedDir = cmStrCat( - "$ENV{DESTDIR}", ConvertToAbsoluteDestination(this->Destination), '/'); - std::string installedFile = cmStrCat(installedDir, this->FileName); - os << indent << "if(EXISTS \"" << installedFile << "\")\n"; - Indent indentN = indent.Next(); - Indent indentNN = indentN.Next(); - Indent indentNNN = indentNN.Next(); - /* clang-format off */ - os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n" - << indentN << " \"" << installedFile << "\"\n" - << indentN << " \"" << this->MainImportFile << "\")\n"; - os << indentN << "if(_cmake_export_file_changed)\n"; - os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir - << this->EFGen->GetConfigImportFileGlob() << "\")\n"; - os << indentNN << "if(_cmake_old_config_files)\n"; - os << indentNNN << "string(REPLACE \";\" \", \" _cmake_old_config_files_text \"${_cmake_old_config_files}\")\n"; - os << indentNNN << R"(message(STATUS "Old export file \")" << installedFile - << "\\\" will be replaced. Removing files [${_cmake_old_config_files_text}].\")\n"; - os << indentNNN << "unset(_cmake_old_config_files_text)\n"; - os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n"; - os << indentNN << "endif()\n"; - os << indentNN << "unset(_cmake_old_config_files)\n"; - os << indentN << "endif()\n"; - os << indentN << "unset(_cmake_export_file_changed)\n"; - os << indent << "endif()\n"; - /* clang-format on */ + auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob(); + if (!configImportFilesGlob.empty()) { + // Remove old per-configuration export files if the main changes. + std::string installedDir = cmStrCat( + "$ENV{DESTDIR}", ConvertToAbsoluteDestination(this->Destination), '/'); + std::string installedFile = cmStrCat(installedDir, this->FileName); + os << indent << "if(EXISTS \"" << installedFile << "\")\n"; + Indent indentN = indent.Next(); + Indent indentNN = indentN.Next(); + Indent indentNNN = indentNN.Next(); + os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n" + << indentN << " \"" << installedFile << "\"\n" + << indentN << " \"" << this->MainImportFile << "\")\n"; + os << indentN << "if(_cmake_export_file_changed)\n"; + os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir + << configImportFilesGlob << "\")\n"; + os << indentNN << "if(_cmake_old_config_files)\n"; + os << indentNNN + << "string(REPLACE \";\" \", \" _cmake_old_config_files_text " + "\"${_cmake_old_config_files}\")\n"; + os << indentNNN << R"(message(STATUS "Old export file \")" << installedFile + << "\\\" will be replaced. " + "Removing files [${_cmake_old_config_files_text}].\")\n"; + os << indentNNN << "unset(_cmake_old_config_files_text)\n"; + os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n"; + os << indentNN << "endif()\n"; + os << indentNN << "unset(_cmake_old_config_files)\n"; + os << indentN << "endif()\n"; + os << indentN << "unset(_cmake_export_file_changed)\n"; + os << indent << "endif()\n"; + } // Install the main export file. std::vector<std::string> files; diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index 5f92851..6100610 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -17,19 +17,18 @@ class cmListFileBacktrace; class cmLocalGenerator; /** \class cmInstallExportGenerator - * \brief Generate rules for creating an export files. + * \brief Support class for generating rules for creating export files. */ class cmInstallExportGenerator : public cmInstallGenerator { public: - cmInstallExportGenerator(cmExportSet* exportSet, std::string const& dest, - std::string file_permissions, - const std::vector<std::string>& configurations, - std::string const& component, MessageLevel message, - bool exclude_from_all, std::string filename, - std::string name_space, - std::string cxx_modules_directory, bool exportOld, - bool android, bool exportPackageDependencies, + cmInstallExportGenerator(cmExportSet* exportSet, std::string destination, + std::string filePermissions, + std::vector<std::string> const& configurations, + std::string component, MessageLevel message, + bool excludeFromAll, std::string filename, + std::string targetNamespace, + std::string cxxModulesDirectory, cmListFileBacktrace backtrace); cmInstallExportGenerator(const cmInstallExportGenerator&) = delete; ~cmInstallExportGenerator() override; @@ -37,6 +36,8 @@ public: cmInstallExportGenerator& operator=(const cmInstallExportGenerator&) = delete; + virtual char const* InstallSubcommand() const = 0; + cmExportSet* GetExportSet() { return this->ExportSet; } bool Compute(cmLocalGenerator* lg) override; @@ -60,8 +61,6 @@ protected: void GenerateScript(std::ostream& os) override; void GenerateScriptConfigs(std::ostream& os, Indent indent) override; void GenerateScriptActions(std::ostream& os, Indent indent) override; - void GenerateImportFile(cmExportSet const* exportSet); - void GenerateImportFile(const char* config, cmExportSet const* exportSet); std::string TempDirCalculate() const; void ComputeTempDir(); @@ -70,8 +69,6 @@ protected: std::string const FileName; std::string const Namespace; std::string const CxxModulesDirectory; - bool const ExportOld; - bool const ExportPackageDependencies; cmLocalGenerator* LocalGenerator = nullptr; std::string TempDir; @@ -345,8 +345,11 @@ CMAKE_CXX_SOURCES="\ cmExecuteProcessCommand \ cmExpandedCommandArgument \ cmExperimental \ + cmExportBuildCMakeConfigGenerator \ cmExportBuildFileGenerator \ + cmExportCMakeConfigGenerator \ cmExportFileGenerator \ + cmExportInstallCMakeConfigGenerator \ cmExportInstallFileGenerator \ cmExportSet \ cmExportTryCompileFileGenerator \ @@ -408,6 +411,7 @@ CMAKE_CXX_SOURCES="\ cmIncludeGuardCommand \ cmIncludeDirectoryCommand \ cmIncludeRegularExpressionCommand \ + cmInstallCMakeConfigExportGenerator \ cmInstallCommand \ cmInstallCommandArguments \ cmInstallCxxModuleBmiGenerator \ |