| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmExportBuildFileGenerator.h" | |
| #include <algorithm> | |
| #include <memory> | |
| #include <set> | |
| #include <sstream> | |
| #include <utility> | |
| #include "cmExportSet.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorFileSets.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmList.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetExport.h" | |
| #include "cmValue.h" | |
| class cmSourceFile; | |
| cmExportBuildFileGenerator::cmExportBuildFileGenerator( | |
| cmDiagnosticContext context) | |
| : Context(std::move(context)) | |
| { | |
| this->LG = nullptr; | |
| this->ExportSet = nullptr; | |
| } | |
| cmDiagnosticContext cmExportBuildFileGenerator::CaptureContext( | |
| cmMakefile const& mf) | |
| { | |
| cmDiagnosticContext context{ mf.GetBacktrace() }; | |
| context.RecordDiagnostic(cmDiagnostics::CMD_AUTHOR, mf.GetStateSnapshot()); | |
| return context; | |
| } | |
| void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg) | |
| { | |
| this->LG = lg; | |
| if (this->ExportSet) { | |
| this->ExportSet->Compute(lg); | |
| } | |
| } | |
| cm::TargetType cmExportBuildFileGenerator::GetExportTargetType( | |
| cmGeneratorTarget const* target) const | |
| { | |
| cm::TargetType targetType = target->GetType(); | |
| // An object library exports as an interface library if we cannot | |
| // tell clients where to find the objects. This is sufficient | |
| // to support transitive usage requirements on other targets that | |
| // use the object library. | |
| if (targetType == cm::TargetType::OBJECT_LIBRARY && | |
| !target->Target->HasKnownObjectFileLocation(nullptr)) { | |
| targetType = cm::TargetType::INTERFACE_LIBRARY; | |
| } | |
| return targetType; | |
| } | |
| void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet) | |
| { | |
| this->ExportSet = exportSet; | |
| } | |
| void cmExportBuildFileGenerator::SetImportLocationProperty( | |
| std::string const& config, std::string const& suffix, | |
| cmGeneratorTarget* target, ImportPropertyMap& properties) | |
| { | |
| // Get the makefile in which to lookup target information. | |
| cmMakefile* mf = target->Makefile; | |
| if (target->GetType() == cm::TargetType::OBJECT_LIBRARY) { | |
| std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix); | |
| // Compute all the object files inside this target and setup | |
| // IMPORTED_OBJECTS as a list of object files | |
| std::vector<cmSourceFile const*> objectSources; | |
| target->GetObjectSources(objectSources, config); | |
| std::string const obj_dir = target->GetObjectDirectory(config); | |
| std::vector<std::string> objects; | |
| for (cmSourceFile const* sf : objectSources) { | |
| std::string const& obj = target->GetObjectName(sf); | |
| objects.push_back(obj_dir + obj); | |
| } | |
| // Store the property. | |
| properties[prop] = cmList::to_string(objects); | |
| } else { | |
| // Add the main target file. | |
| { | |
| std::string prop = cmStrCat("IMPORTED_LOCATION", suffix); | |
| std::string value; | |
| if (target->IsAppBundleOnApple()) { | |
| value = | |
| target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact); | |
| } else { | |
| value = target->GetFullPath(config, | |
| cmStateEnums::RuntimeBinaryArtifact, true); | |
| } | |
| properties[prop] = value; | |
| } | |
| // Add the import library for windows DLLs. | |
| if (target->HasImportLibrary(config)) { | |
| std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix); | |
| std::string value = | |
| target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact, true); | |
| if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) { | |
| target->GetImplibGNUtoMS(config, value, value, | |
| "${CMAKE_IMPORT_LIBRARY_SUFFIX}"); | |
| } | |
| properties[prop] = value; | |
| } | |
| } | |
| } | |
| 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) { | |
| auto const& exportInfo = this->FindExportInfo(dependee); | |
| if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) { | |
| std::string missingTarget = *exportInfo.Namespaces.begin(); | |
| missingTarget += dependee->GetExportName(); | |
| link_libs += missingTarget; | |
| this->MissingTargets.emplace_back(std::move(missingTarget)); | |
| return; | |
| } | |
| // We are not appending, so all exported targets should be | |
| // known here. This is probably user-error. | |
| this->ComplainAboutMissingTarget(depender, dependee, exportInfo); | |
| } | |
| // Assume the target will be exported by another command. | |
| // Append it with the export namespace. | |
| link_libs += this->Namespace; | |
| link_libs += dependee->GetExportName(); | |
| } | |
| void cmExportBuildFileGenerator::GetTargets( | |
| std::vector<TargetExport>& targets) const | |
| { | |
| if (this->ExportSet) { | |
| for (std::unique_ptr<cmTargetExport> const& te : | |
| this->ExportSet->GetTargetExports()) { | |
| if (te->NamelinkOnly) { | |
| continue; | |
| } | |
| targets.emplace_back(te->TargetName, te->XcFrameworkLocation); | |
| } | |
| return; | |
| } | |
| targets = this->Targets; | |
| } | |
| cmExportFileGenerator::ExportInfo cmExportBuildFileGenerator::FindExportInfo( | |
| cmGeneratorTarget const* target) const | |
| { | |
| return target->GetLocalGenerator() | |
| ->GetGlobalGenerator() | |
| ->FindBuildExportInfo(target); | |
| } | |
| cm::optional<cmExportBuildFileGenerator::ExportRecord> | |
| cmExportBuildFileGenerator::FindRecordForTarget( | |
| cmGeneratorTarget const* target) const | |
| { | |
| auto const& name = target->GetName(); | |
| std::vector<TargetExport> targets; | |
| this->GetTargets(targets); | |
| bool const contains = | |
| std::any_of(targets.begin(), targets.end(), | |
| [&name](TargetExport const& te) { return te.Name == name; }); | |
| cm::optional<ExportRecord> result; | |
| if (contains) { | |
| ExportRecord rec; | |
| rec.Name = this->ExportSet ? this->ExportSet->GetName() : std::string{}; | |
| rec.Namespace = this->GetNamespace(); | |
| result = rec; | |
| } | |
| return result; | |
| } | |
| void cmExportBuildFileGenerator::ComplainAboutMissingTarget( | |
| cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee, | |
| ExportInfo const& exportInfo) const | |
| { | |
| std::ostringstream e; | |
| e << "export called with target \"" << depender->GetName() | |
| << "\" which requires target \"" << dependee->GetName() << "\" "; | |
| if (exportInfo.Sets.empty()) { | |
| e << "that is not in any export set."; | |
| } else { | |
| if (exportInfo.Sets.size() == 1) { | |
| e << "that is not in this export set, but in another export set which " | |
| "is " | |
| "exported multiple times with different namespaces: "; | |
| } else { | |
| e << "that is not in this export set, but in multiple other export " | |
| "sets: "; | |
| } | |
| e << cmJoin(exportInfo.Files, ", ") << ".\n" | |
| << "An exported target cannot depend upon another target which is " | |
| "exported in more than one export set or with more than one " | |
| "namespace. Consider consolidating the exports of the \"" | |
| << dependee->GetName() << "\" target to a single export."; | |
| } | |
| this->ReportError(e.str()); | |
| } | |
| void cmExportBuildFileGenerator::ComplainAboutDuplicateTarget( | |
| std::string const& targetName) const | |
| { | |
| this->ReportError( | |
| cmStrCat("given target \"", targetName, "\" more than once.")); | |
| } | |
| void cmExportBuildFileGenerator::IssueMessage(MessageType type, | |
| std::string const& message) const | |
| { | |
| this->LG->GetMakefile()->IssueMessage(type, message); | |
| } | |
| void cmExportBuildFileGenerator::IssueDiagnostic( | |
| cmDiagnosticCategory category, std::string const& message) const | |
| { | |
| this->LG->GetMakefile()->IssueDiagnostic(category, message, this->Context); | |
| } | |
| std::string cmExportBuildFileGenerator::InstallNameDir( | |
| 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->GetInstallNameDirForBuildTree(config); | |
| } | |
| return install_name_dir; | |
| } | |
| bool cmExportBuildFileGenerator::PopulateInterfaceProperties( | |
| cmGeneratorTarget const* target, ImportPropertyMap& properties) | |
| { | |
| 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); | |
| } | |
| bool cmExportBuildFileGenerator::PopulateFileSetInterfaceProperties( | |
| cmGeneratorTarget const* target, ImportFileSetPropertyMap& properties) | |
| { | |
| cmGeneratorFileSets const* const gfs = target->GetGeneratorFileSets(); | |
| bool result = true; | |
| for (auto const& type : gfs->GetInterfaceFileSetTypes()) { | |
| for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) { | |
| ImportPropertyMap& fsProperties = properties[fileSet->GetName()]; | |
| this->PopulateFileSetInterfaceProperty( | |
| "INTERFACE_INCLUDE_DIRECTORIES", target, fileSet, | |
| cmGeneratorExpression::BuildInterface, fsProperties); | |
| result = result && | |
| this->PopulateFileSetInterfaceProperties( | |
| target, fileSet, cmGeneratorExpression::InstallInterface, | |
| fsProperties); | |
| } | |
| } | |
| return result; | |
| } |