diff options
Diffstat (limited to 'Source/cmExportFileGenerator.cxx')
-rw-r--r-- | Source/cmExportFileGenerator.cxx | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx new file mode 100644 index 000000000..eb19df5e2 --- /dev/null +++ b/Source/cmExportFileGenerator.cxx @@ -0,0 +1,443 @@ +/*============================================================================ + 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 <cmsys/auto_ptr.hxx> + +//---------------------------------------------------------------------------- +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<std::ofstream> foutPtr; + if(this->AppendMode) + { + // Open for append. + cmsys::auto_ptr<std::ofstream> + ap(new std::ofstream(this->MainImportFile.c_str(), std::ios::app)); + foutPtr = ap; + } + else + { + // Generate atomically and with copy-if-different. + cmsys::auto_ptr<cmGeneratedFileStream> + 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 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)) + { + 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<std::string> 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<std::string>::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"; + } + + 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::GenerateImportedFileCheckLoop(std::ostream& os) +{ + // Add code which verifies at cmake time that the file which is being + // imported actually exists on disk. This should in theory always be theory + // case, but still when packages are split into normal and development + // packages this might get broken (e.g. the Config.cmake could be part of + // the non-development package, something similar happened to me without + // on SUSE with a mysql pkg-config file, which claimed everything is fine, + // but the development package was not installed.). + os << "# Loop over all imported files and verify that they actually exist\n" + "FOREACH(target ${_IMPORT_CHECK_TARGETS} )\n" + " FOREACH(file ${_IMPORT_CHECK_FILES_FOR_${target}} )\n" + " IF(NOT EXISTS \"${file}\" )\n" + " MESSAGE(FATAL_ERROR \"The imported target \\\"${target}\\\"" + " references the file\n" + " \\\"${file}\\\"\n" + "but this file does not exist. Possible reasons include:\n" + "* The file was deleted, renamed, or moved to another location.\n" + "* An install or uninstall procedure did not complete successfully.\n" + "* The installation package was faulty and contained\n" + " \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n" + "but not all the files it references.\n" + "\")\n" + " ENDIF()\n" + " ENDFOREACH()\n" + " UNSET(_IMPORT_CHECK_FILES_FOR_${target})\n" + "ENDFOREACH()\n" + "UNSET(_IMPORT_CHECK_TARGETS)\n" + "\n"; +} + + +//---------------------------------------------------------------------------- +void +cmExportFileGenerator +::GenerateImportedFileChecksCode(std::ostream& os, cmTarget* target, + ImportPropertyMap const& properties, + const std::set<std::string>& 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<std::string>::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"; +} |