/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmExportInstallPackageInfoGenerator.h" #include #include #include #include #include #include #include #include #include #include #include #include "cmAlgorithms.h" #include "cmDiagnostics.h" #include "cmExportSet.h" #include "cmFileSetMetadata.h" #include "cmGenExContext.h" #include "cmGeneratorExpression.h" #include "cmGeneratorFileSet.h" #include "cmGeneratorTarget.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" #include "cmList.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPackageInfoArguments.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetExport.h" #include "cmTargetTypes.h" cmExportInstallPackageInfoGenerator::cmExportInstallPackageInfoGenerator( cmInstallExportGenerator* iegen, cmPackageInfoArguments arguments) : cmExportPackageInfoGenerator(std::move(arguments)) , cmExportInstallFileGenerator(iegen) { } std::string cmExportInstallPackageInfoGenerator::GetConfigImportFileGlob() const { std::string glob = cmStrCat(this->FileBase, "@*", this->FileExt); return glob; } std::string const& cmExportInstallPackageInfoGenerator::GetExportName() const { return this->GetPackageName(); } bool cmExportInstallPackageInfoGenerator::GenerateMainFile(std::ostream& os) { std::vector allTargets; { auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); }; if (!this->CollectExports(visitor)) { return false; } } if (!this->CheckPackage()) { return false; } Json::Value root = this->GeneratePackageInfo(); Json::Value& components = root["components"]; // Compute the relative import prefix for the file std::string const& packagePath = this->GenerateImportPrefix(); if (packagePath.empty()) { return false; } root["cps_path"] = packagePath; // Create all the imported targets. for (cmTargetExport const* te : allTargets) { cmGeneratorTarget* gt = te->Target; cm::TargetType targetType = this->GetExportTargetType(te); Json::Value* const component = this->GenerateImportTarget(components, gt, targetType); if (!component) { return false; } ImportPropertyMap properties; if (!this->PopulateInterfaceProperties(te, properties)) { return false; } this->PopulateInterfaceLinkLibrariesProperty( gt, cmGeneratorExpression::InstallInterface, properties); if (targetType != cm::TargetType::INTERFACE_LIBRARY) { this->RequiresConfigFiles = true; } // De-duplicate include directories prior to generation. auto it = properties.find("INTERFACE_INCLUDE_DIRECTORIES"); if (it != properties.end()) { auto list = cmList{ it->second }; list = cmList{ list.begin(), cmRemoveDuplicates(list) }; properties["INTERFACE_INCLUDE_DIRECTORIES"] = list.to_string(); } // Set configuration-agnostic properties for component. this->GenerateInterfaceProperties(*component, gt, properties); if (!this->GenerateFileSetProperties(*component, gt, te, packagePath)) { return false; } this->GenerateTargetFileSets(*component, gt, te); } // Generate per-configuration files before the main file so that // processing link-dependent targets populates package requirements. bool result = true; if (this->RequiresConfigFiles) { for (std::string const& c : this->Configurations) { if (!this->GenerateImportFileConfig(c)) { result = false; } } } this->GeneratePackageRequires(root); // Write the primary packing information file. this->WritePackageInfo(root, os); return result; } void cmExportInstallPackageInfoGenerator::GenerateImportTargetsConfig( std::ostream& os, std::string const& config, std::string const& suffix) { Json::Value root; root["name"] = this->GetPackageName(); root["configuration"] = (config.empty() ? "noconfig" : config); std::string const& packagePath = this->GenerateImportPrefix(); Json::Value& components = root["components"]; for (auto const& te : this->GetExportSet()->GetTargetExports()) { // Collect import properties for this target. ImportPropertyMap properties; std::set importedLocations; if (this->GetExportTargetType(te.get()) != cm::TargetType::INTERFACE_LIBRARY) { this->PopulateImportProperties(config, suffix, te.get(), properties, importedLocations); } Json::Value component = this->GenerateInterfaceConfigProperties(suffix, properties); this->GenerateFileSetProperties(component, te->Target, te.get(), packagePath, config); if (!component.empty()) { components[te->Target->GetExportName()] = std::move(component); } } this->WritePackageInfo(root, os); } std::string cmExportInstallPackageInfoGenerator::GenerateImportPrefix() const { std::string expDest = this->IEGen->GetDestination(); if (cmSystemTools::FileIsFullPath(expDest)) { std::string const& installPrefix = this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition( "CMAKE_INSTALL_PREFIX"); if (cmHasPrefix(expDest, installPrefix)) { auto n = installPrefix.length(); while (n < expDest.length() && expDest[n] == '/') { ++n; } expDest = expDest.substr(n); } else { this->ReportError( cmStrCat("install(PACKAGE_INFO \"", this->GetExportName(), "\" ...) specifies DESTINATION \"", expDest, "\" which is not a subdirectory of the install prefix.")); return {}; } } if (expDest.empty()) { return this->GetInstallPrefix(); } return cmStrCat(this->GetImportPrefixWithSlash(), expDest); } std::string cmExportInstallPackageInfoGenerator::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->GetInstallNameDirForInstallTree(config, "@prefix@"); } return install_name_dir; } std::string cmExportInstallPackageInfoGenerator::GetCxxModulesDirectory() const { return IEGen->GetCxxModuleDirectory(); } cm::optional cmExportInstallPackageInfoGenerator::GetFileSetDirectory( cmGeneratorTarget const* target, cmTargetExport const* targetExport, cmGeneratorFileSet const* fileSet, cm::optional const& config) { cmInstallFileSetGenerator const* const fsg = targetExport->FileSetGenerators.at(fileSet->GetName()); cmInstallFileSetGenerator::DestinationContext const result = fsg->GetDestination(target, config.value_or("")); if (!config == result.HadContextSensitiveCondition) { return {}; } // Use cm::optional here to enable NRVO. cm::optional dest = cmOutputConverter::EscapeForCMake( result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap); if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) { dest = cmStrCat("@prefix@/"_s, *dest); } return dest; } bool cmExportInstallPackageInfoGenerator::GenerateFileSetProperties( Json::Value& component, cmGeneratorTarget* gte, cmTargetExport const* te, std::string const& packagePath, cm::optional config) { bool hasModules = false; std::set seenIncludeDirectories; for (auto const& name : gte->Target->GetAllInterfaceFileSets()) { cmGeneratorFileSet const* fileSet = gte->GetFileSet(name); if (!fileSet) { this->IssueMessage(MessageType::FATAL_ERROR, cmStrCat("File set \"", name, "\" is listed in interface file sets of ", gte->GetName(), " but has not been created")); return false; } cm::optional const& fileSetDirectory = this->GetFileSetDirectory(gte, te, fileSet, config); if (fileSet->GetType() == cm::FileSetMetadata::HEADERS) { if (!fileSetDirectory) { if (!config) { this->RequiresConfigFiles = true; } } else if (!cm::contains(seenIncludeDirectories, *fileSetDirectory)) { component["includes"].append(*fileSetDirectory); seenIncludeDirectories.insert(*fileSetDirectory); } } else if (fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) { hasModules = true; this->RequiresConfigFiles = true; } } if (hasModules && config) { std::string const manifestPath = this->GenerateCxxModules(component, gte, packagePath, *config); if (!manifestPath.empty()) { this->ConfigCxxModuleFiles[*config] = cmStrCat(this->FileDir, '/', manifestPath); } } return true; } void cmExportInstallPackageInfoGenerator::GenerateTargetFileSets( Json::Value& fileSets, cmGeneratorTarget const* target, cmGeneratorFileSet const* fileSet, cmTargetExport const* targetExport, std::string const& type) const { cm::optional dest = this->GetFileSetDirectory(target, targetExport, fileSet); if (!dest) { this->IssueDiagnostic( cmDiagnostics::CMD_AUTHOR, cmStrCat("The \""_s, target->GetName(), "\" target's interface file set \""_s, fileSet->GetName(), "\" of type \""_s, fileSet->GetType(), "\" has a context-sensitive destination, which is not " "supported. The file set will not be exported."_s)); return; } cm::GenEx::Context context{ target->LocalGenerator, {} }; std::vector files; auto eval = [&files](std::string&& /*baseDir*/, std::string&& relPath, std::string&& /*file*/) { files.emplace_back(std::move(relPath)); }; if (fileSet->EvaluateFiles(context, target, eval)) { this->IssueDiagnostic( cmDiagnostics::CMD_AUTHOR, cmStrCat("The \""_s, target->GetName(), "\" target's interface file set \""_s, fileSet->GetName(), "\" of type \""_s, fileSet->GetType(), "\" contains context-sensitive information, which is not " "supported. The file set will not be exported."_s)); return; } this->GenerateTargetFileSet(fileSets, fileSet, type, *dest, files); }