| /*========================================================================= |
| |
| Program: CMake - Cross-Platform Makefile Generator |
| Module: $RCSfile: cmInstallTargetGenerator.cxx,v $ |
| Language: C++ |
| Date: $Date: 2012/03/29 17:21:08 $ |
| Version: $Revision: 1.1.1.1 $ |
| |
| Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. |
| See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even |
| the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| PURPOSE. See the above copyright notices for more information. |
| |
| =========================================================================*/ |
| #include "cmInstallTargetGenerator.h" |
| |
| #include "cmComputeLinkInformation.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmLocalGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmake.h" |
| |
| #include <assert.h> |
| |
| //---------------------------------------------------------------------------- |
| cmInstallTargetGenerator |
| ::cmInstallTargetGenerator(cmTarget& t, const char* dest, bool implib, |
| const char* file_permissions, |
| std::vector<std::string> const& configurations, |
| const char* component, bool optional): |
| cmInstallGenerator(dest, configurations, component), Target(&t), |
| ImportLibrary(implib), FilePermissions(file_permissions), Optional(optional) |
| { |
| this->NamelinkMode = NamelinkModeNone; |
| this->Target->SetHaveInstallRule(true); |
| } |
| |
| //---------------------------------------------------------------------------- |
| cmInstallTargetGenerator |
| ::~cmInstallTargetGenerator() |
| { |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmInstallTargetGenerator::GenerateScript(std::ostream& os) |
| { |
| // Warn if installing an exclude-from-all target. |
| if(this->Target->GetPropertyAsBool("EXCLUDE_FROM_ALL")) |
| { |
| cmOStringStream msg; |
| msg << "WARNING: Target \"" << this->Target->GetName() |
| << "\" has EXCLUDE_FROM_ALL set and will not be built by default " |
| << "but an install rule has been provided for it. CMake does " |
| << "not define behavior for this case."; |
| cmSystemTools::Message(msg.str().c_str(), "Warning"); |
| } |
| |
| // Track indentation. |
| Indent indent; |
| |
| // Begin this block of installation. |
| std::string component_test = |
| this->CreateComponentTest(this->Component.c_str()); |
| os << indent << "IF(" << component_test << ")\n"; |
| |
| // Compute the build tree directory from which to copy the target. |
| std::string fromDir; |
| if(this->Target->NeedRelinkBeforeInstall()) |
| { |
| fromDir = this->Target->GetMakefile()->GetStartOutputDirectory(); |
| fromDir += cmake::GetCMakeFilesDirectory(); |
| fromDir += "/CMakeRelink.dir/"; |
| } |
| else |
| { |
| fromDir = this->Target->GetDirectory(0, this->ImportLibrary); |
| fromDir += "/"; |
| } |
| |
| // Generate a portion of the script for each configuration. |
| if(this->ConfigurationTypes->empty()) |
| { |
| this->GenerateScriptForConfig(os, fromDir.c_str(), |
| this->ConfigurationName, |
| indent.Next()); |
| } |
| else |
| { |
| for(std::vector<std::string>::const_iterator i = |
| this->ConfigurationTypes->begin(); |
| i != this->ConfigurationTypes->end(); ++i) |
| { |
| this->GenerateScriptForConfig(os, fromDir.c_str(), i->c_str(), |
| indent.Next()); |
| } |
| } |
| |
| // End this block of installation. |
| os << indent << "ENDIF(" << component_test << ")\n\n"; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os, |
| const char* fromDir, |
| const char* config, |
| Indent const& indent) |
| { |
| // Compute the per-configuration directory containing the files. |
| std::string fromDirConfig = fromDir; |
| this->Target->GetMakefile()->GetLocalGenerator()->GetGlobalGenerator() |
| ->AppendDirectoryForConfig("", config, "/", fromDirConfig); |
| |
| if(config && *config) |
| { |
| // Skip this configuration for config-specific installation that |
| // does not match it. |
| if(!this->InstallsForConfig(config)) |
| { |
| return; |
| } |
| |
| // Generate a per-configuration block. |
| std::string config_test = this->CreateConfigTest(config); |
| os << indent << "IF(" << config_test << ")\n"; |
| this->GenerateScriptForConfigDir(os, fromDirConfig.c_str(), config, |
| indent.Next()); |
| os << indent << "ENDIF(" << config_test << ")\n"; |
| } |
| else |
| { |
| this->GenerateScriptForConfigDir(os, fromDirConfig.c_str(), config, |
| indent); |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmInstallTargetGenerator |
| ::GenerateScriptForConfigDir(std::ostream& os, |
| const char* fromDirConfig, |
| const char* config, |
| Indent const& indent) |
| { |
| // Compute the full path to the main installed file for this target. |
| NameType nameType = this->ImportLibrary? NameImplib : NameNormal; |
| std::string toInstallPath = this->GetInstallDestination(); |
| toInstallPath += "/"; |
| toInstallPath += this->GetInstallFilename(this->Target, config, nameType); |
| |
| // Track whether post-install operations should be added to the |
| // script. |
| bool tweakInstalledFile = true; |
| |
| // Compute the list of files to install for this target. |
| std::vector<std::string> files; |
| std::string literal_args; |
| cmTarget::TargetType type = this->Target->GetType(); |
| if(type == cmTarget::EXECUTABLE) |
| { |
| // There is a bug in cmInstallCommand if this fails. |
| assert(this->NamelinkMode == NamelinkModeNone); |
| |
| std::string targetName; |
| std::string targetNameReal; |
| std::string targetNameImport; |
| std::string targetNamePDB; |
| this->Target->GetExecutableNames(targetName, targetNameReal, |
| targetNameImport, targetNamePDB, |
| config); |
| if(this->ImportLibrary) |
| { |
| std::string from1 = fromDirConfig; |
| from1 += targetNameImport; |
| files.push_back(from1); |
| |
| // An import library looks like a static library. |
| type = cmTarget::STATIC_LIBRARY; |
| } |
| else |
| { |
| std::string from1 = fromDirConfig; |
| from1 += targetName; |
| |
| // Handle OSX Bundles. |
| if(this->Target->IsAppBundleOnApple()) |
| { |
| // Compute the source locations of the bundle executable and |
| // Info.plist file. |
| from1 += ".app"; |
| files.push_back(from1); |
| type = cmTarget::INSTALL_DIRECTORY; |
| // Need to apply install_name_tool and stripping to binary |
| // inside bundle. |
| toInstallPath += ".app/Contents/MacOS/"; |
| toInstallPath += |
| this->GetInstallFilename(this->Target, config, nameType); |
| literal_args += " USE_SOURCE_PERMISSIONS"; |
| } |
| else |
| { |
| // Operations done at install time on the installed file should |
| // be done on the real file and not any of the symlinks. |
| toInstallPath = this->GetInstallDestination(); |
| toInstallPath += "/"; |
| toInstallPath += targetNameReal; |
| |
| files.push_back(from1); |
| if(targetNameReal != targetName) |
| { |
| std::string from2 = fromDirConfig; |
| from2 += targetNameReal; |
| files.push_back(from2); |
| } |
| } |
| } |
| } |
| else |
| { |
| std::string targetName; |
| std::string targetNameSO; |
| std::string targetNameReal; |
| std::string targetNameImport; |
| std::string targetNamePDB; |
| this->Target->GetLibraryNames(targetName, targetNameSO, targetNameReal, |
| targetNameImport, targetNamePDB, |
| config); |
| if(this->ImportLibrary) |
| { |
| // There is a bug in cmInstallCommand if this fails. |
| assert(this->NamelinkMode == NamelinkModeNone); |
| |
| std::string from1 = fromDirConfig; |
| from1 += targetNameImport; |
| files.push_back(from1); |
| |
| // An import library looks like a static library. |
| type = cmTarget::STATIC_LIBRARY; |
| } |
| else if(this->Target->IsFrameworkOnApple()) |
| { |
| // There is a bug in cmInstallCommand if this fails. |
| assert(this->NamelinkMode == NamelinkModeNone); |
| |
| // Compute the build tree location of the framework directory |
| std::string from1 = fromDirConfig; |
| from1 += targetName; |
| from1 += ".framework"; |
| files.push_back(from1); |
| |
| type = cmTarget::INSTALL_DIRECTORY; |
| |
| // Need to apply install_name_tool and stripping to binary |
| // inside framework. |
| toInstallPath += ".framework/Versions/"; |
| toInstallPath += this->Target->GetFrameworkVersion(); |
| toInstallPath += "/"; |
| toInstallPath += this->GetInstallFilename(this->Target, config, |
| NameNormal); |
| |
| literal_args += " USE_SOURCE_PERMISSIONS"; |
| } |
| else |
| { |
| // Operations done at install time on the installed file should |
| // be done on the real file and not any of the symlinks. |
| toInstallPath = this->GetInstallDestination(); |
| toInstallPath += "/"; |
| toInstallPath += targetNameReal; |
| |
| // Construct the list of file names to install for this library. |
| bool haveNamelink = false; |
| std::string fromName; |
| std::string fromSOName; |
| std::string fromRealName; |
| fromName = fromDirConfig; |
| fromName += targetName; |
| if(targetNameSO != targetName) |
| { |
| haveNamelink = true; |
| fromSOName = fromDirConfig; |
| fromSOName += targetNameSO; |
| } |
| if(targetNameReal != targetName && |
| targetNameReal != targetNameSO) |
| { |
| haveNamelink = true; |
| fromRealName = fromDirConfig; |
| fromRealName += targetNameReal; |
| } |
| |
| // Add the names based on the current namelink mode. |
| if(haveNamelink) |
| { |
| // With a namelink we need to check the mode. |
| if(this->NamelinkMode == NamelinkModeOnly) |
| { |
| // Install the namelink only. |
| files.push_back(fromName); |
| tweakInstalledFile = false; |
| } |
| else |
| { |
| // Install the real file if it has its own name. |
| if(!fromRealName.empty()) |
| { |
| files.push_back(fromRealName); |
| } |
| |
| // Install the soname link if it has its own name. |
| if(!fromSOName.empty()) |
| { |
| files.push_back(fromSOName); |
| } |
| |
| // Install the namelink if it is not to be skipped. |
| if(this->NamelinkMode != NamelinkModeSkip) |
| { |
| files.push_back(fromName); |
| } |
| } |
| } |
| else |
| { |
| // Without a namelink there will be only one file. Install it |
| // if this is not a namelink-only rule. |
| if(this->NamelinkMode != NamelinkModeOnly) |
| { |
| files.push_back(fromName); |
| } |
| } |
| } |
| } |
| |
| // Skip this rule if no files are to be installed for the target. |
| if(files.empty()) |
| { |
| return; |
| } |
| |
| // Construct the path of the file on disk after installation on |
| // which tweaks may be performed. |
| std::string toDestDirPath = "$ENV{DESTDIR}"; |
| if(toInstallPath[0] != '/' && toInstallPath[0] != '$') |
| { |
| toDestDirPath += "/"; |
| } |
| toDestDirPath += toInstallPath; |
| |
| // Add pre-installation tweaks. |
| if(tweakInstalledFile) |
| { |
| // Collect tweaking rules. |
| cmOStringStream tw; |
| this->AddRPathCheckRule(tw, indent.Next(), config, toDestDirPath); |
| std::string tws = tw.str(); |
| |
| // Add the rules, if any. |
| if(!tws.empty()) |
| { |
| os << indent << "IF(EXISTS \"" << toDestDirPath << "\")\n"; |
| os << tws; |
| os << indent << "ENDIF(EXISTS \"" << toDestDirPath << "\")\n"; |
| } |
| } |
| |
| // Write code to install the target file. |
| const char* no_dir_permissions = 0; |
| const char* no_rename = 0; |
| const char* no_properties = 0; |
| bool optional = this->Optional || this->ImportLibrary; |
| this->AddInstallRule(os, type, files, |
| optional, no_properties, |
| this->FilePermissions.c_str(), no_dir_permissions, |
| no_rename, literal_args.c_str(), |
| indent); |
| |
| // Add post-installation tweaks. |
| if(tweakInstalledFile) |
| { |
| // Collect tweaking rules. |
| cmOStringStream tw; |
| this->AddInstallNamePatchRule(tw, indent.Next(), config, toDestDirPath); |
| this->AddChrpathPatchRule(tw, indent.Next(), config, toDestDirPath); |
| this->AddRanlibRule(tw, indent.Next(), type, toDestDirPath); |
| this->AddStripRule(tw, indent.Next(), type, toDestDirPath); |
| std::string tws = tw.str(); |
| |
| // Add the rules, if any. |
| if(!tws.empty()) |
| { |
| os << indent << "IF(EXISTS \"" << toDestDirPath << "\")\n"; |
| os << tws; |
| os << indent << "ENDIF(EXISTS \"" << toDestDirPath << "\")\n"; |
| } |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string |
| cmInstallTargetGenerator::GetInstallFilename(const char* config) const |
| { |
| NameType nameType = this->ImportLibrary? NameImplib : NameNormal; |
| return |
| cmInstallTargetGenerator::GetInstallFilename(this->Target, config, |
| nameType); |
| } |
| |
| //---------------------------------------------------------------------------- |
| std::string cmInstallTargetGenerator::GetInstallFilename(cmTarget* target, |
| const char* config, |
| NameType nameType) |
| { |
| std::string fname; |
| // Compute the name of the library. |
| if(target->GetType() == cmTarget::EXECUTABLE) |
| { |
| std::string targetName; |
| std::string targetNameReal; |
| std::string targetNameImport; |
| std::string targetNamePDB; |
| target->GetExecutableNames(targetName, targetNameReal, |
| targetNameImport, targetNamePDB, |
| config); |
| if(nameType == NameImplib) |
| { |
| // Use the import library name. |
| fname = targetNameImport; |
| } |
| else if(nameType == NameReal) |
| { |
| // Use the canonical name. |
| fname = targetNameReal; |
| } |
| else |
| { |
| // Use the canonical name. |
| fname = targetName; |
| } |
| } |
| else |
| { |
| std::string targetName; |
| std::string targetNameSO; |
| std::string targetNameReal; |
| std::string targetNameImport; |
| std::string targetNamePDB; |
| target->GetLibraryNames(targetName, targetNameSO, targetNameReal, |
| targetNameImport, targetNamePDB, config); |
| if(nameType == NameImplib) |
| { |
| // Use the import library name. |
| fname = targetNameImport; |
| } |
| else if(nameType == NameSO) |
| { |
| // Use the soname. |
| fname = targetNameSO; |
| } |
| else if(nameType == NameReal) |
| { |
| // Use the real name. |
| fname = targetNameReal; |
| } |
| else |
| { |
| // Use the canonical name. |
| fname = targetName; |
| } |
| } |
| |
| return fname; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmInstallTargetGenerator |
| ::AddInstallNamePatchRule(std::ostream& os, Indent const& indent, |
| const char* config, std::string const& toDestDirPath) |
| { |
| if(this->ImportLibrary || |
| !(this->Target->GetType() == cmTarget::SHARED_LIBRARY || |
| this->Target->GetType() == cmTarget::MODULE_LIBRARY || |
| this->Target->GetType() == cmTarget::EXECUTABLE)) |
| { |
| return; |
| } |
| |
| // Fix the install_name settings in installed binaries. |
| std::string installNameTool = |
| this->Target->GetMakefile()->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL"); |
| |
| if(!installNameTool.size()) |
| { |
| return; |
| } |
| |
| // Build a map of build-tree install_name to install-tree install_name for |
| // shared libraries linked to this target. |
| std::map<cmStdString, cmStdString> install_name_remap; |
| if(cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config)) |
| { |
| std::set<cmTarget*> const& sharedLibs = cli->GetSharedLibrariesLinked(); |
| for(std::set<cmTarget*>::const_iterator j = sharedLibs.begin(); |
| j != sharedLibs.end(); ++j) |
| { |
| // If the build tree and install tree use different path |
| // components of the install_name field then we need to create a |
| // mapping to be applied after installation. |
| cmTarget* tgt = *j; |
| std::string for_build = tgt->GetInstallNameDirForBuildTree(config); |
| std::string for_install = tgt->GetInstallNameDirForInstallTree(config); |
| if(for_build != for_install) |
| { |
| // The directory portions differ. Append the filename to |
| // create the mapping. |
| std::string fname = |
| this->GetInstallFilename(tgt, config, NameSO); |
| |
| // Map from the build-tree install_name. |
| for_build += fname; |
| |
| // Map to the install-tree install_name. |
| for_install += fname; |
| |
| // Store the mapping entry. |
| install_name_remap[for_build] = for_install; |
| } |
| } |
| } |
| |
| // Edit the install_name of the target itself if necessary. |
| std::string new_id; |
| if(this->Target->GetType() == cmTarget::SHARED_LIBRARY) |
| { |
| std::string for_build = |
| this->Target->GetInstallNameDirForBuildTree(config); |
| std::string for_install = |
| this->Target->GetInstallNameDirForInstallTree(config); |
| |
| if(this->Target->IsFrameworkOnApple() && for_install.empty()) |
| { |
| // Frameworks seem to have an id corresponding to their own full |
| // path. |
| // ... |
| // for_install = fullDestPath_without_DESTDIR_or_name; |
| } |
| |
| // If the install name will change on installation set the new id |
| // on the installed file. |
| if(for_build != for_install) |
| { |
| // Prepare to refer to the install-tree install_name. |
| new_id = for_install; |
| new_id += this->GetInstallFilename(this->Target, config, NameSO); |
| } |
| } |
| |
| // Write a rule to run install_name_tool to set the install-tree |
| // install_name value and references. |
| if(!new_id.empty() || !install_name_remap.empty()) |
| { |
| os << indent << "EXECUTE_PROCESS(COMMAND \"" << installNameTool; |
| os << "\""; |
| if(!new_id.empty()) |
| { |
| os << "\n" << indent << " -id \"" << new_id << "\""; |
| } |
| for(std::map<cmStdString, cmStdString>::const_iterator |
| i = install_name_remap.begin(); |
| i != install_name_remap.end(); ++i) |
| { |
| os << "\n" << indent << " -change \"" |
| << i->first << "\" \"" << i->second << "\""; |
| } |
| os << "\n" << indent << " \"" << toDestDirPath << "\")\n"; |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmInstallTargetGenerator |
| ::AddRPathCheckRule(std::ostream& os, Indent const& indent, |
| const char* config, std::string const& toDestDirPath) |
| { |
| // Skip the chrpath if the target does not need it. |
| if(this->ImportLibrary || !this->Target->IsChrpathUsed()) |
| { |
| return; |
| } |
| |
| // Get the link information for this target. |
| // It can provide the RPATH. |
| cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config); |
| if(!cli) |
| { |
| return; |
| } |
| |
| // Get the install RPATH from the link information. |
| std::string newRpath = cli->GetChrpathString(); |
| |
| // Write a rule to remove the installed file if its rpath is not the |
| // new rpath. This is needed for existing build/install trees when |
| // the installed rpath changes but the file is not rebuilt. |
| os << indent << "FILE(RPATH_CHECK\n" |
| << indent << " FILE \"" << toDestDirPath << "\"\n" |
| << indent << " RPATH \"" << newRpath << "\")\n"; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmInstallTargetGenerator |
| ::AddChrpathPatchRule(std::ostream& os, Indent const& indent, |
| const char* config, std::string const& toDestDirPath) |
| { |
| // Skip the chrpath if the target does not need it. |
| if(this->ImportLibrary || !this->Target->IsChrpathUsed()) |
| { |
| return; |
| } |
| |
| // Get the link information for this target. |
| // It can provide the RPATH. |
| cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config); |
| if(!cli) |
| { |
| return; |
| } |
| |
| // Construct the original rpath string to be replaced. |
| std::string oldRpath = cli->GetRPathString(false); |
| |
| // Get the install RPATH from the link information. |
| std::string newRpath = cli->GetChrpathString(); |
| |
| // Skip the rule if the paths are identical |
| if(oldRpath == newRpath) |
| { |
| return; |
| } |
| |
| // Write a rule to run chrpath to set the install-tree RPATH |
| if(newRpath.empty()) |
| { |
| os << indent << "FILE(RPATH_REMOVE\n" |
| << indent << " FILE \"" << toDestDirPath << "\")\n"; |
| } |
| else |
| { |
| os << indent << "FILE(RPATH_CHANGE\n" |
| << indent << " FILE \"" << toDestDirPath << "\"\n" |
| << indent << " OLD_RPATH \"" << oldRpath << "\"\n" |
| << indent << " NEW_RPATH \"" << newRpath << "\")\n"; |
| } |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmInstallTargetGenerator::AddStripRule(std::ostream& os, |
| Indent const& indent, |
| cmTarget::TargetType type, |
| const std::string& toDestDirPath) |
| { |
| |
| // don't strip static libraries, because it removes the only symbol table |
| // they have so you can't link to them anymore |
| if(type == cmTarget::STATIC_LIBRARY) |
| { |
| return; |
| } |
| |
| // Don't handle OSX Bundles. |
| if(this->Target->GetMakefile()->IsOn("APPLE") && |
| this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) |
| { |
| return; |
| } |
| |
| if(! this->Target->GetMakefile()->IsSet("CMAKE_STRIP")) |
| { |
| return; |
| } |
| |
| os << indent << "IF(CMAKE_INSTALL_DO_STRIP)\n"; |
| os << indent << " EXECUTE_PROCESS(COMMAND \"" |
| << this->Target->GetMakefile()->GetDefinition("CMAKE_STRIP") |
| << "\" \"" << toDestDirPath << "\")\n"; |
| os << indent << "ENDIF(CMAKE_INSTALL_DO_STRIP)\n"; |
| } |
| |
| //---------------------------------------------------------------------------- |
| void |
| cmInstallTargetGenerator::AddRanlibRule(std::ostream& os, |
| Indent const& indent, |
| cmTarget::TargetType type, |
| const std::string& toDestDirPath) |
| { |
| // Static libraries need ranlib on this platform. |
| if(type != cmTarget::STATIC_LIBRARY) |
| { |
| return; |
| } |
| |
| // Perform post-installation processing on the file depending |
| // on its type. |
| if(!this->Target->GetMakefile()->IsOn("APPLE")) |
| { |
| return; |
| } |
| |
| std::string ranlib = |
| this->Target->GetMakefile()->GetRequiredDefinition("CMAKE_RANLIB"); |
| if(ranlib.empty()) |
| { |
| return; |
| } |
| |
| os << indent << "EXECUTE_PROCESS(COMMAND \"" |
| << ranlib << "\" \"" << toDestDirPath << "\")\n"; |
| } |