/*============================================================================ 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 "cmExportFileGenerator.h" #include "cmExportSet.h" #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" #include "cmInstallExportGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetExport.h" #include "cmVersion.h" #include "cmComputeLinkInformation.h" #include #include "assert.h" //---------------------------------------------------------------------------- cmExportFileGenerator::cmExportFileGenerator() { this->AppendMode = false; } //---------------------------------------------------------------------------- void cmExportFileGenerator::AddConfiguration(const char* config) { this->Configurations.push_back(config); } //---------------------------------------------------------------------------- void cmExportFileGenerator::SetExportFile(const char* mainFile) { this->MainImportFile = mainFile; this->FileDir = cmSystemTools::GetFilenamePath(this->MainImportFile); this->FileBase = cmSystemTools::GetFilenameWithoutLastExtension(this->MainImportFile); this->FileExt = cmSystemTools::GetFilenameLastExtension(this->MainImportFile); } //---------------------------------------------------------------------------- bool cmExportFileGenerator::GenerateImportFile() { // Open the output file to generate it. cmsys::auto_ptr foutPtr; if(this->AppendMode) { // Open for append. cmsys::auto_ptr ap(new std::ofstream(this->MainImportFile.c_str(), std::ios::app)); foutPtr = ap; } else { // Generate atomically and with copy-if-different. cmsys::auto_ptr ap(new cmGeneratedFileStream(this->MainImportFile.c_str(), true)); ap->SetCopyIfDifferent(true); foutPtr = ap; } if(!foutPtr.get() || !*foutPtr) { std::string se = cmSystemTools::GetLastSystemError(); cmOStringStream e; e << "cannot write to file \"" << this->MainImportFile << "\": " << se; cmSystemTools::Error(e.str().c_str()); return false; } std::ostream& os = *foutPtr; // Protect that file against use with older CMake versions. os << "# Generated by CMake " << cmVersion::GetCMakeVersion() << "\n\n"; os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.5)\n" << " message(FATAL_ERROR \"CMake >= 2.6.0 required\")\n" << "endif()\n"; // Isolate the file policy level. // We use 2.6 here instead of the current version because newer // versions of CMake should be able to export files imported by 2.6 // until the import format changes. os << "cmake_policy(PUSH)\n" << "cmake_policy(VERSION 2.6)\n"; // Start with the import file header. this->GenerateImportHeaderCode(os); // Create all the imported targets. bool result = this->GenerateMainFile(os); // End with the import file footer. this->GenerateImportFooterCode(os); os << "cmake_policy(POP)\n"; return result; } //---------------------------------------------------------------------------- void cmExportFileGenerator::GenerateImportConfig(std::ostream& os, const char* config, std::vector &missingTargets) { // Construct the property configuration suffix. std::string suffix = "_"; if(config && *config) { suffix += cmSystemTools::UpperCase(config); } else { suffix += "NOCONFIG"; } // Generate the per-config target information. this->GenerateImportTargetsConfig(os, config, suffix, missingTargets); } //---------------------------------------------------------------------------- void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName, cmTarget *target, ImportPropertyMap &properties) { const char *input = target->GetProperty(propName); if (input) { properties[propName] = input; } } //---------------------------------------------------------------------------- void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName, const char *outputName, cmTarget *target, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap &properties, std::vector &missingTargets) { const char *input = target->GetProperty(propName); if (input) { if (!*input) { // Set to empty properties[outputName] = ""; return; } std::string prepro = cmGeneratorExpression::Preprocess(input, preprocessRule); if (!prepro.empty()) { this->ResolveTargetsInGeneratorExpressions(prepro, target, propName, missingTargets); properties[outputName] = prepro; } } } //---------------------------------------------------------------------------- void cmExportFileGenerator::PopulateInterfaceProperty(const char *propName, cmTarget *target, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap &properties, std::vector &missingTargets) { this->PopulateInterfaceProperty(propName, propName, target, preprocessRule, properties, missingTargets); } //---------------------------------------------------------------------------- void getPropertyContents(cmTarget *tgt, const char *prop, std::set &ifaceProperties) { const char *p = tgt->GetProperty(prop); if (!p) { return; } std::vector content; cmSystemTools::ExpandListArgument(p, content); for (std::vector::const_iterator ci = content.begin(); ci != content.end(); ++ci) { ifaceProperties.insert(*ci); } } //---------------------------------------------------------------------------- void getCompatibleInterfaceProperties(cmTarget *target, std::set &ifaceProperties, const char *config) { cmComputeLinkInformation *info = target->GetLinkInformation(config); const cmComputeLinkInformation::ItemVector &deps = info->GetItems(); for(cmComputeLinkInformation::ItemVector::const_iterator li = deps.begin(); li != deps.end(); ++li) { if (!li->Target) { continue; } getPropertyContents(li->Target, "COMPATIBLE_INTERFACE_BOOL", ifaceProperties); getPropertyContents(li->Target, "COMPATIBLE_INTERFACE_STRING", ifaceProperties); } } //---------------------------------------------------------------------------- void cmExportFileGenerator::PopulateCompatibleInterfaceProperties( cmTarget *target, ImportPropertyMap &properties) { this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_BOOL", target, properties); this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_STRING", target, properties); std::set ifaceProperties; getPropertyContents(target, "COMPATIBLE_INTERFACE_BOOL", ifaceProperties); getPropertyContents(target, "COMPATIBLE_INTERFACE_STRING", ifaceProperties); getCompatibleInterfaceProperties(target, ifaceProperties, 0); std::vector configNames; target->GetMakefile()->GetConfigurations(configNames); for (std::vector::const_iterator ci = configNames.begin(); ci != configNames.end(); ++ci) { getCompatibleInterfaceProperties(target, ifaceProperties, ci->c_str()); } for (std::set::const_iterator it = ifaceProperties.begin(); it != ifaceProperties.end(); ++it) { this->PopulateInterfaceProperty(("INTERFACE_" + *it).c_str(), target, properties); } } //---------------------------------------------------------------------------- void cmExportFileGenerator::GenerateInterfaceProperties(cmTarget *target, std::ostream& os, const ImportPropertyMap &properties) { if (!properties.empty()) { os << "if(NOT ${CMAKE_FIND_PACKAGE_NAME}_NO_INTERFACES)\n"; std::string targetName = this->Namespace; targetName += target->GetName(); os << " set_target_properties(" << targetName << " PROPERTIES\n"; for(ImportPropertyMap::const_iterator pi = properties.begin(); pi != properties.end(); ++pi) { os << " " << pi->first << " \"" << pi->second << "\"\n"; } os << " )\nendif()\n\n"; } } //---------------------------------------------------------------------------- bool cmExportFileGenerator::AddTargetNamespace(std::string &input, cmTarget* target, std::vector &missingTargets) { cmMakefile *mf = target->GetMakefile(); cmTarget *tgt = mf->FindTargetToUse(input.c_str()); if (!tgt) { return false; } if(tgt->IsImported()) { return true; } if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) { input = this->Namespace + input; } else { std::string namespacedTarget; this->HandleMissingTarget(namespacedTarget, missingTargets, mf, target, tgt); if (!namespacedTarget.empty()) { input = namespacedTarget; } } return true; } //---------------------------------------------------------------------------- static bool isGeneratorExpression(const std::string &lib) { const std::string::size_type openpos = lib.find("$<"); return (openpos != std::string::npos) && (lib.find(">", openpos) != std::string::npos); } //---------------------------------------------------------------------------- void cmExportFileGenerator::ResolveTargetsInGeneratorExpressions( std::string &input, cmTarget* target, const char *propName, std::vector &missingTargets, FreeTargetsReplace replace) { if (replace == NoReplaceFreeTargets) { this->ResolveTargetsInGeneratorExpression(input, target, propName, missingTargets); return; } std::vector parts; cmGeneratorExpression::Split(input, parts); std::string sep; input = ""; for(std::vector::iterator li = parts.begin(); li != parts.end(); ++li) { if (!isGeneratorExpression(*li)) { this->AddTargetNamespace(*li, target, missingTargets); } else { this->ResolveTargetsInGeneratorExpression( *li, target, propName, missingTargets); } input += sep + *li; sep = ";"; } } //---------------------------------------------------------------------------- void cmExportFileGenerator::ResolveTargetsInGeneratorExpression( std::string &input, cmTarget* target, const char *propName, std::vector &missingTargets) { std::string::size_type pos = 0; std::string::size_type lastPos = pos; cmMakefile *mf = target->GetMakefile(); while((pos = input.find("$", nameStartPos); std::string::size_type commaPos = input.find(",", nameStartPos); std::string::size_type nextOpenPos = input.find("$<", nameStartPos); if (commaPos == input.npos // Implied 'this' target || closePos == input.npos // Imcomplete expression. || closePos < commaPos // Implied 'this' target || nextOpenPos < commaPos) // Non-literal { lastPos = nameStartPos; continue; } std::string targetName = input.substr(nameStartPos, commaPos - nameStartPos); if (this->AddTargetNamespace(targetName, target, missingTargets)) { input.replace(nameStartPos, commaPos - nameStartPos, targetName); } lastPos = nameStartPos + targetName.size() + 1; } std::string errorString; pos = 0; lastPos = pos; while((pos = input.find("$", nameStartPos); if (endPos == input.npos) { errorString = "$ expression incomplete"; break; } std::string targetName = input.substr(nameStartPos, endPos - nameStartPos); if(targetName.find("$<") != input.npos) { errorString = "$ requires its parameter to be a " "literal."; break; } if (this->AddTargetNamespace(targetName, target, missingTargets)) { assert(propName); // The link libraries strings will // never contain $ std::string replacement = "$IssueMessage(cmake::FATAL_ERROR, errorString); return; } pos = 0; lastPos = pos; while((pos = input.find("$", nameStartPos); if (endPos == input.npos) { errorString = "$ expression incomplete"; break; } std::string targetName = input.substr(nameStartPos, endPos - nameStartPos); if(targetName.find("$<") != input.npos) { errorString = "$ requires its parameter to be a " "literal."; break; } if (!this->AddTargetNamespace(targetName, target, missingTargets)) { errorString = "$ requires its parameter to be a " "reachable target."; break; } input.replace(pos, endPos - pos + 1, targetName); lastPos = endPos; } this->ReplaceInstallPrefix(input); if (!errorString.empty()) { mf->IssueMessage(cmake::FATAL_ERROR, errorString); } } //---------------------------------------------------------------------------- void cmExportFileGenerator::ReplaceInstallPrefix(std::string &) { // Do nothing } //---------------------------------------------------------------------------- void cmExportFileGenerator ::SetImportLinkInterface(const char* config, std::string const& suffix, cmGeneratorExpression::PreprocessContext preprocessRule, cmTarget* target, ImportPropertyMap& properties, std::vector& missingTargets) { // Add the transitive link dependencies for this configuration. cmTarget::LinkInterface const* iface = target->GetLinkInterface(config, target); if (!iface) { return; } if (iface->ImplementationIsInterface) { this->SetImportLinkProperty(suffix, target, "IMPORTED_LINK_INTERFACE_LIBRARIES", iface->Libraries, properties, missingTargets); return; } const char *propContent; if (const char *prop_suffixed = target->GetProperty( ("LINK_INTERFACE_LIBRARIES" + suffix).c_str())) { propContent = prop_suffixed; } else if (const char *prop = target->GetProperty( "LINK_INTERFACE_LIBRARIES")) { propContent = prop; } else { return; } if (!*propContent) { properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = ""; return; } std::string prepro = cmGeneratorExpression::Preprocess(propContent, preprocessRule); if (!prepro.empty()) { this->ResolveTargetsInGeneratorExpressions(prepro, target, 0, missingTargets, ReplaceFreeTargets); properties["IMPORTED_LINK_INTERFACE_LIBRARIES" + suffix] = prepro; } } //---------------------------------------------------------------------------- void cmExportFileGenerator ::SetImportDetailProperties(const char* config, std::string const& suffix, cmTarget* target, ImportPropertyMap& properties, std::vector& missingTargets ) { // Get the makefile in which to lookup target information. cmMakefile* mf = target->GetMakefile(); // Add the soname for unix shared libraries. if(target->GetType() == cmTarget::SHARED_LIBRARY || target->GetType() == cmTarget::MODULE_LIBRARY) { // Check whether this is a DLL platform. bool dll_platform = (mf->IsOn("WIN32") || mf->IsOn("CYGWIN") || mf->IsOn("MINGW")); if(!dll_platform) { std::string prop; std::string value; if(target->HasSOName(config)) { prop = "IMPORTED_SONAME"; value = target->GetSOName(config); } else { prop = "IMPORTED_NO_SONAME"; value = "TRUE"; } prop += suffix; properties[prop] = value; } } // Add the transitive link dependencies for this configuration. if(cmTarget::LinkInterface const* iface = target->GetLinkInterface(config, target)) { this->SetImportLinkProperty(suffix, target, "IMPORTED_LINK_INTERFACE_LANGUAGES", iface->Languages, properties, missingTargets); this->SetImportLinkProperty(suffix, target, "IMPORTED_LINK_DEPENDENT_LIBRARIES", iface->SharedDeps, properties, missingTargets); if(iface->Multiplicity > 0) { std::string prop = "IMPORTED_LINK_INTERFACE_MULTIPLICITY"; prop += suffix; cmOStringStream m; m << iface->Multiplicity; properties[prop] = m.str(); } } } //---------------------------------------------------------------------------- void cmExportFileGenerator ::SetImportLinkProperty(std::string const& suffix, cmTarget* target, const char* propName, std::vector const& libs, ImportPropertyMap& properties, std::vector& missingTargets ) { // Skip the property if there are no libraries. if(libs.empty()) { return; } // Construct the property value. std::string link_libs; const char* sep = ""; for(std::vector::const_iterator li = libs.begin(); li != libs.end(); ++li) { // Separate this from the previous entry. link_libs += sep; sep = ";"; std::string temp = *li; this->AddTargetNamespace(temp, target, missingTargets); link_libs += temp; } // Store the property. std::string prop = propName; prop += suffix; properties[prop] = link_libs; } //---------------------------------------------------------------------------- void cmExportFileGenerator::GenerateImportHeaderCode(std::ostream& os, const char* config) { os << "#----------------------------------------------------------------\n" << "# Generated CMake target import file"; if(config) { 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. os << "# Commands may need to know the format version.\n" << "set(CMAKE_IMPORT_FILE_VERSION 1)\n" << "\n"; } //---------------------------------------------------------------------------- void cmExportFileGenerator::GenerateExpectedTargetsCode(std::ostream& os, const std::string &expectedTargets) { os << "set(_targetsDefined)\n" "set(_targetsNotDefined)\n" "set(_expectedTargets)\n" "foreach(_expectedTarget " << expectedTargets << ")\n" " list(APPEND _expectedTargets ${_expectedTarget})\n" " if(NOT TARGET ${_expectedTarget})\n" " list(APPEND _targetsNotDefined ${_expectedTarget})\n" " endif()\n" " if(TARGET ${_expectedTarget})\n" " list(APPEND _targetsDefined ${_expectedTarget})\n" " endif()\n" "endforeach()\n" "if(\"${_targetsDefined}\" STREQUAL \"${_expectedTargets}\")\n" " set(CMAKE_IMPORT_FILE_VERSION)\n" " cmake_policy(POP)\n" " return()\n" "endif()\n" "if(NOT \"${_targetsDefined}\" STREQUAL \"\")\n" " message(FATAL_ERROR \"Some (but not all) targets in this export " "set were already defined.\\nTargets Defined: ${_targetsDefined}\\n" "Targets not yet defined: ${_targetsNotDefined}\\n\")\n" "endif()\n" "unset(_targetsDefined)\n" "unset(_targetsNotDefined)\n" "unset(_expectedTargets)\n" "\n\n"; } //---------------------------------------------------------------------------- void cmExportFileGenerator ::GenerateImportTargetCode(std::ostream& os, cmTarget* target) { // Construct the imported target name. std::string targetName = this->Namespace; targetName += target->GetName(); // Create the imported target. os << "# Create imported target " << targetName << "\n"; switch(target->GetType()) { case cmTarget::EXECUTABLE: os << "add_executable(" << targetName << " IMPORTED)\n"; break; case cmTarget::STATIC_LIBRARY: os << "add_library(" << targetName << " STATIC IMPORTED)\n"; break; case cmTarget::SHARED_LIBRARY: os << "add_library(" << targetName << " SHARED IMPORTED)\n"; break; case cmTarget::MODULE_LIBRARY: os << "add_library(" << targetName << " MODULE IMPORTED)\n"; break; default: // should never happen break; } // Mark the imported executable if it has exports. if(target->IsExecutableWithExports()) { 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"; } os << "\n"; } //---------------------------------------------------------------------------- void cmExportFileGenerator ::GenerateImportPropertyCode(std::ostream& os, const char* config, cmTarget* target, ImportPropertyMap const& properties) { // Construct the imported target name. std::string targetName = this->Namespace; targetName += target->GetName(); // Set the import properties. os << "# Import target \"" << targetName << "\" for configuration \"" << config << "\"\n"; os << "set_property(TARGET " << targetName << " APPEND PROPERTY IMPORTED_CONFIGURATIONS "; if(config && *config) { os << cmSystemTools::UpperCase(config); } else { os << "NOCONFIG"; } os << ")\n"; os << "set_target_properties(" << targetName << " PROPERTIES\n"; for(ImportPropertyMap::const_iterator pi = properties.begin(); pi != properties.end(); ++pi) { os << " " << pi->first << " \"" << pi->second << "\"\n"; } os << " )\n" << "\n"; } //---------------------------------------------------------------------------- void cmExportFileGenerator::GenerateMissingTargetsCheckCode(std::ostream& os, const std::vector& missingTargets) { if (missingTargets.empty()) { return; } os << "# Make sure the targets which have been exported in some other \n" "# export set exist.\n"; std::set emitted; for(unsigned int i=0; i& importedLocations) { // Construct the imported target name. std::string targetName = this->Namespace; targetName += target->GetName(); os << "list(APPEND _IMPORT_CHECK_TARGETS " << targetName << " )\n" "list(APPEND _IMPORT_CHECK_FILES_FOR_" << targetName << " "; for(std::set::const_iterator li = importedLocations.begin(); li != importedLocations.end(); ++li) { ImportPropertyMap::const_iterator pi = properties.find(*li); if (pi != properties.end()) { os << "\"" << pi->second << "\" "; } } os << ")\n\n"; }