diff options
Diffstat (limited to 'Source/cmExportInstallFileGenerator.cxx')
-rw-r--r-- | Source/cmExportInstallFileGenerator.cxx | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx new file mode 100644 index 0000000..b695904 --- /dev/null +++ b/Source/cmExportInstallFileGenerator.cxx @@ -0,0 +1,559 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmExportInstallFileGenerator.h" + +#include "cmExportSet.h" +#include "cmExportSetMap.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallTargetGenerator.h" +#include "cmTargetExport.h" +#include "cmAlgorithms.h" + +//---------------------------------------------------------------------------- +cmExportInstallFileGenerator +::cmExportInstallFileGenerator(cmInstallExportGenerator* iegen): + IEGen(iegen) +{ +} + +//---------------------------------------------------------------------------- +std::string cmExportInstallFileGenerator::GetConfigImportFileGlob() +{ + std::string glob = this->FileBase; + glob += "-*"; + glob += this->FileExt; + return glob; +} + +//---------------------------------------------------------------------------- +bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) +{ + std::vector<cmTargetExport*> allTargets; + { + std::string expectedTargets; + std::string sep; + for(std::vector<cmTargetExport*>::const_iterator + tei = this->IEGen->GetExportSet()->GetTargetExports()->begin(); + tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei) + { + expectedTargets += + sep + this->Namespace + (*tei)->Target->GetExportName(); + sep = " "; + cmTargetExport * te = *tei; + if(this->ExportedTargets.insert(te->Target).second) + { + allTargets.push_back(te); + } + 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().c_str()); + return false; + } + } + + this->GenerateExpectedTargetsCode(os, expectedTargets); + } + + // 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. + os << + "# The installation prefix configured by this project.\n" + "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n" + "\n"; + } + 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.c_str(), "/lib/") || + cmHasLiteralPrefix(absDestS.c_str(), "/lib64/") || + cmHasLiteralPrefix(absDestS.c_str(), "/usr/lib/") || + cmHasLiteralPrefix(absDestS.c_str(), "/usr/lib64/")) + { + // Handle "/usr move" symlinks created by some Linux distros. + 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"; + } + std::string dest = expDest; + while(!dest.empty()) + { + os << + "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" PATH)\n"; + dest = cmSystemTools::GetFilenamePath(dest); + } + os << "\n"; + } + + std::vector<std::string> missingTargets; + + bool require2_8_12 = false; + bool require3_0_0 = false; + bool require3_1_0 = false; + bool requiresConfigFiles = false; + // Create all the imported targets. + for(std::vector<cmTargetExport*>::const_iterator + tei = allTargets.begin(); + tei != allTargets.end(); ++tei) + { + cmGeneratorTarget* gt = (*tei)->Target; + + requiresConfigFiles = requiresConfigFiles + || gt->GetType() != cmState::INTERFACE_LIBRARY; + + this->GenerateImportTargetCode(os, gt); + + ImportPropertyMap properties; + + this->PopulateIncludeDirectoriesInterface(*tei, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + this->PopulateSourcesInterface(*tei, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", + gt, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", + gt, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", + gt, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", + gt, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", + gt, + cmGeneratorExpression::InstallInterface, + properties, missingTargets); + + const bool newCMP0022Behavior = + gt->GetPolicyStatusCMP0022() != cmPolicies::WARN + && gt->GetPolicyStatusCMP0022() != cmPolicies::OLD; + if (newCMP0022Behavior) + { + if (this->PopulateInterfaceLinkLibrariesProperty(gt, + cmGeneratorExpression::InstallInterface, + properties, missingTargets) + && !this->ExportOld) + { + require2_8_12 = true; + } + } + if (gt->GetType() == cmState::INTERFACE_LIBRARY) + { + require3_0_0 = true; + } + if(gt->GetProperty("INTERFACE_SOURCES")) + { + // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1 + // can consume them. + require3_1_0 = true; + } + + this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", + gt, properties); + + this->PopulateCompatibleInterfaceProperties(gt, properties); + + this->GenerateInterfaceProperties(gt, os, properties); + } + + if (require3_1_0) + { + this->GenerateRequiredCMakeVersion(os, "3.1.0"); + } + else if (require3_0_0) + { + this->GenerateRequiredCMakeVersion(os, "3.0.0"); + } + else if (require2_8_12) + { + this->GenerateRequiredCMakeVersion(os, "2.8.12"); + } + + // Now load per-configuration properties for them. + os << "# Load information for each installed configuration.\n" + << "get_filename_component(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n" + << "file(GLOB CONFIG_FILES \"${_DIR}/" + << this->GetConfigImportFileGlob() << "\")\n" + << "foreach(f ${CONFIG_FILES})\n" + << " include(${f})\n" + << "endforeach()\n" + << "\n"; + + // Cleanup the import prefix variable. + os << "# Cleanup temporary variables.\n" + << "set(_IMPORT_PREFIX)\n" + << "\n"; + this->GenerateImportedFileCheckLoop(os); + + bool result = true; + // Generate an import file for each configuration. + // Don't do this if we only export INTERFACE_LIBRARY targets. + if (requiresConfigFiles) + { + for(std::vector<std::string>::const_iterator + ci = this->Configurations.begin(); + ci != this->Configurations.end(); ++ci) + { + if(!this->GenerateImportFileConfig(*ci, missingTargets)) + { + result = false; + } + } + } + + this->GenerateMissingTargetsCheckCode(os, missingTargets); + + return result; +} + +//---------------------------------------------------------------------------- +void +cmExportInstallFileGenerator::ReplaceInstallPrefix(std::string &input) +{ + std::string::size_type pos = 0; + std::string::size_type lastPos = pos; + + while((pos = input.find("$<INSTALL_PREFIX>", lastPos)) != input.npos) + { + std::string::size_type endPos = pos + sizeof("$<INSTALL_PREFIX>") - 1; + input.replace(pos, endPos - pos, "${_IMPORT_PREFIX}"); + lastPos = endPos; + } +} + +//---------------------------------------------------------------------------- +bool +cmExportInstallFileGenerator::GenerateImportFileConfig( + const std::string& config, + std::vector<std::string> &missingTargets) +{ + // 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 = this->FileDir; + fileName += "/"; + fileName += this->FileBase; + fileName += "-"; + if(!config.empty()) + { + fileName += cmSystemTools::LowerCase(config); + } + else + { + fileName += "noconfig"; + } + fileName += this->FileExt; + + // Open the output file to generate it. + cmGeneratedFileStream exportFileStream(fileName.c_str(), true); + if(!exportFileStream) + { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName + << "\": " << se; + cmSystemTools::Error(e.str().c_str()); + return false; + } + std::ostream& os = exportFileStream; + + // Start with the import file header. + this->GenerateImportHeaderCode(os, config); + + // Generate the per-config target information. + this->GenerateImportConfig(os, config, missingTargets); + + // 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, + std::vector<std::string> &missingTargets) +{ + // Add each target in the set to the export. + for(std::vector<cmTargetExport*>::const_iterator + tei = this->IEGen->GetExportSet()->GetTargetExports()->begin(); + tei != this->IEGen->GetExportSet()->GetTargetExports()->end(); ++tei) + { + // Collect import properties for this target. + cmTargetExport const* te = *tei; + if (te->Target->GetType() == cmState::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->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, missingTargets); + + this->SetImportLinkInterface(config, suffix, + cmGeneratorExpression::InstallInterface, + gtgt, properties, missingTargets); + + // TOOD: 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. + this->GenerateImportPropertyCode(os, config, gtgt, properties); + this->GenerateImportedFileChecksCode(os, gtgt, properties, + importedLocations); + } + } +} + +//---------------------------------------------------------------------------- +void +cmExportInstallFileGenerator +::SetImportLocationProperty(const std::string& config, + std::string const& suffix, + cmInstallTargetGenerator* itgen, + ImportPropertyMap& properties, + std::set<std::string>& importedLocations + ) +{ + // Skip rules that do not match this configuration. + if(!(itgen && itgen->InstallsForConfig(config))) + { + return; + } + + // Get the target to be installed. + cmGeneratorTarget* target = itgen->GetTarget(); + + // Construct the installed location of the target. + std::string dest = itgen->GetDestination(config); + std::string value; + if(!cmSystemTools::FileIsFullPath(dest.c_str())) + { + // The target is installed relative to the installation prefix. + value = "${_IMPORT_PREFIX}/"; + } + value += dest; + value += "/"; + + if(itgen->IsImportLibrary()) + { + // Construct the property name. + std::string prop = "IMPORTED_IMPLIB"; + prop += suffix; + + // Append the installed file name. + value += itgen->GetInstallFilename(target, config, + cmInstallTargetGenerator::NameImplib); + + // Store the property. + properties[prop] = value; + importedLocations.insert(prop); + } + else + { + // Construct the property name. + std::string prop = "IMPORTED_LOCATION"; + prop += suffix; + + // Append the installed file name. + if(target->IsAppBundleOnApple()) + { + value += itgen->GetInstallFilename(target, config); + value += ".app/Contents/MacOS/"; + value += itgen->GetInstallFilename(target, config); + } + else + { + value += itgen->GetInstallFilename(target, config, + cmInstallTargetGenerator::NameReal); + } + + // Store the property. + properties[prop] = value; + importedLocations.insert(prop); + } +} + +//---------------------------------------------------------------------------- +void +cmExportInstallFileGenerator::HandleMissingTarget(std::string& link_libs, + std::vector<std::string>& missingTargets, + cmGeneratorTarget* depender, cmGeneratorTarget* dependee) +{ + const std::string name = dependee->GetName(); + cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator(); + std::vector<std::string> namespaces = this->FindNamespaces(gg, name); + int targetOccurrences = (int)namespaces.size(); + if (targetOccurrences == 1) + { + std::string missingTarget = namespaces[0]; + + missingTarget += dependee->GetExportName(); + link_libs += missingTarget; + missingTargets.push_back(missingTarget); + } + else + { + // All exported targets should be known here and should be unique. + // This is probably user-error. + this->ComplainAboutMissingTarget(depender, dependee, targetOccurrences); + } +} + +//---------------------------------------------------------------------------- +std::vector<std::string> +cmExportInstallFileGenerator +::FindNamespaces(cmGlobalGenerator* gg, const std::string& name) +{ + std::vector<std::string> namespaces; + const cmExportSetMap& exportSets = gg->GetExportSets(); + + for(cmExportSetMap::const_iterator expIt = exportSets.begin(); + expIt != exportSets.end(); + ++expIt) + { + const cmExportSet* exportSet = expIt->second; + std::vector<cmTargetExport*> const* targets = + exportSet->GetTargetExports(); + + bool containsTarget = false; + for(unsigned int i=0; i<targets->size(); i++) + { + if (name == (*targets)[i]->Target->GetName()) + { + containsTarget = true; + break; + } + } + + if (containsTarget) + { + std::vector<cmInstallExportGenerator const*> const* installs = + exportSet->GetInstallations(); + for(unsigned int i=0; i<installs->size(); i++) + { + namespaces.push_back((*installs)[i]->GetNamespace()); + } + } + } + + return namespaces; +} + +//---------------------------------------------------------------------------- +void +cmExportInstallFileGenerator +::ComplainAboutMissingTarget(cmGeneratorTarget* depender, + cmGeneratorTarget* dependee, + int occurrences) +{ + std::ostringstream e; + e << "install(EXPORT \"" + << this->IEGen->GetExportSet()->GetName() + << "\" ...) " + << "includes target \"" << depender->GetName() + << "\" which requires target \"" << dependee->GetName() << "\" "; + if (occurrences == 0) + { + e << "that is not in the export set."; + } + else + { + e << "that is not in this export set, but " << occurrences + << " times in others."; + } + cmSystemTools::Error(e.str().c_str()); +} + +std::string +cmExportInstallFileGenerator::InstallNameDir(cmGeneratorTarget* target, + const std::string&) +{ + std::string install_name_dir; + + cmMakefile* mf = target->Target->GetMakefile(); + if(mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) + { + install_name_dir = + target->GetInstallNameDirForInstallTree(); + } + + return install_name_dir; +} |