/*============================================================================ 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 "cmGeneratedFileStream.h" #include "cmMakefile.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmVersion.h" #include //---------------------------------------------------------------------------- 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(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.5)\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) { // 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); } //---------------------------------------------------------------------------- void cmExportFileGenerator ::SetImportDetailProperties(const char* config, std::string const& suffix, cmTarget* target, ImportPropertyMap& properties) { // 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 soname = target->GetSOName(config); std::string prop = "IMPORTED_SONAME"; prop += suffix; properties[prop] = soname; } } // Add the transitive link dependencies for this configuration. if(cmTarget::LinkInterface const* iface = target->GetLinkInterface(config)) { this->SetImportLinkProperty(suffix, target, "IMPORTED_LINK_INTERFACE_LANGUAGES", iface->Languages, properties); this->SetImportLinkProperty(suffix, target, "IMPORTED_LINK_INTERFACE_LIBRARIES", iface->Libraries, properties); this->SetImportLinkProperty(suffix, target, "IMPORTED_LINK_DEPENDENT_LIBRARIES", iface->SharedDeps, properties); 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) { // Skip the property if there are no libraries. if(libs.empty()) { return; } // Get the makefile in which to lookup target information. cmMakefile* mf = target->GetMakefile(); // 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 = ";"; // Append this entry. if(cmTarget* tgt = mf->FindTargetToUse(li->c_str())) { // This is a target. if(tgt->IsImported()) { // The target is imported (and therefore is not in the // export). Append the raw name. link_libs += *li; } else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) { // The target is in the export. Append it with the export // namespace. link_libs += this->Namespace; link_libs += *li; } else { // The target is not in the export. if(!this->AppendMode) { // We are not appending, so all exported targets should be // known here. This is probably user-error. this->ComplainAboutMissingTarget(target, tgt); } // Assume the target will be exported by another command. // Append it with the export namespace. link_libs += this->Namespace; link_libs += *li; } } else { // Append the raw name. link_libs += *li; } } // 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 ::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"; } 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"; }