From d392acb4e65e2ce133acf4a07dba983060f58725 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 17 Aug 2005 16:11:18 -0400 Subject: ENH: Added versioned executable support. This partially addresses bug#2143. Also made OUTPUT_NAME work when installing executables. --- Source/cmFileCommand.cxx | 47 +++++++++++++++++--- Source/cmLocalGenerator.cxx | 19 +++++++- Source/cmLocalUnixMakefileGenerator3.cxx | 74 ++++++++++++++++++++++---------- Source/cmSetTargetPropertiesCommand.h | 3 ++ Source/cmTarget.cxx | 71 ++++++++++++++++++++++++++++-- Source/cmTarget.h | 17 ++++++++ 6 files changed, 198 insertions(+), 33 deletions(-) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 7fd5910..64d3bd9 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -570,6 +570,9 @@ bool cmFileCommand::HandleInstallCommand( smanifest_files += soname.substr(destDirLength); } } + + // Reconstruct the source file path taking into account the + // extra directory and possible new file name. cmOStringStream str; str << cmSystemTools::GetFilenamePath(ctarget) << "/"; if ( extra_dir.size() > 0 ) @@ -581,14 +584,48 @@ bool cmFileCommand::HandleInstallCommand( } break; case cmTarget::EXECUTABLE: + { + // Handle executable versioning + const char* exe_version = 0; + if ( properties.find("VERSION") != properties.end() ) + { + exe_version = properties["VERSION"]; + } + if ( exe_version ) + { + std::string exename = destfile; + std::string exename_nopath = fname; + exename_nopath += "-"; + exename_nopath += exe_version; + + fname += "-"; + fname += exe_version; + destfile += "-"; + destfile += exe_version; + + cmSystemTools::RemoveFile(exename.c_str()); + + if (!cmSystemTools::CreateSymlink(exename_nopath.c_str(), exename.c_str()) ) + { + std::string errstring = "error when creating symlink from: " + exename + " to " + exename_nopath; + this->SetError(errstring.c_str()); + return false; + } + smanifest_files += ";"; + smanifest_files += exename.substr(destDirLength); + } + + // Reconstruct the source file path taking into account the + // extra directory and possible new file name. + cmOStringStream str; + str << cmSystemTools::GetFilenamePath(ctarget) << "/"; if ( extra_dir.size() > 0 ) { - cmOStringStream str; - str << cmSystemTools::GetFilenamePath(ctarget) - << "/" << extra_dir << "/" - << fname; - ctarget = str.str(); + str << extra_dir << "/"; } + str << fname; + ctarget = str.str(); + } break; } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 2433414..f0e531d 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -369,6 +369,18 @@ void cmLocalGenerator::GenerateInstallRules() } break; case cmTarget::EXECUTABLE: + { + std::string properties; +#if defined(_WIN32) && !defined(__CYGWIN__) + const char* exe_version = 0; +#else + const char* exe_version = l->second.GetProperty("VERSION"); +#endif + if(exe_version) + { + properties += " VERSION "; + properties += exe_version; + } if(l->second.GetPropertyAsBool("MACOSX_BUNDLE")) { fname = exeOutPath; @@ -385,7 +397,8 @@ void cmLocalGenerator::GenerateInstallRules() pdest += ".app/Contents"; bdest += ".app/Contents/MacOS"; // first install the actual executable - this->AddInstallRule(fout, bdest.c_str(), type, files); + this->AddInstallRule(fout, bdest.c_str(), type, files, + false, properties.c_str()); files = plist.c_str(); // now install the Info.plist file this->AddInstallRule(fout, pdest.c_str(), @@ -396,8 +409,10 @@ void cmLocalGenerator::GenerateInstallRules() fname = exeOutPath; fname += l->second.GetFullName(m_Makefile); files = fname.c_str(); - this->AddInstallRule(fout, dest, type, files); + this->AddInstallRule(fout, dest, type, files, false, + properties.c_str()); } + } break; case cmTarget::INSTALL_FILES: { diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 2ca5731..f397106 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1279,35 +1279,34 @@ cmLocalUnixMakefileGenerator3 // Add a dependency on the rule file itself. this->AppendRuleDepend(depends, ruleFileName); - // Construct the full path to the executable that will be generated. - std::string targetFullPath = m_ExecutableOutputPath; - if(targetFullPath.length() == 0) + // Get the name of the executable to generate. + std::string targetName; + std::string targetNameReal; + target.GetExecutableNames(m_Makefile, targetName, targetNameReal); + + // Construct the full path version of the names. + std::string outpath = m_ExecutableOutputPath; + if(outpath.length() == 0) { - targetFullPath = m_Makefile->GetStartOutputDirectory(); - targetFullPath += "/"; + outpath = m_Makefile->GetStartOutputDirectory(); + outpath += "/"; } #ifdef __APPLE__ if(target.GetPropertyAsBool("MACOSX_BUNDLE")) { // Make bundle directories - targetFullPath += target.GetName(); - targetFullPath += ".app/Contents/MacOS/"; + outpath += target.GetName(); + outpath += ".app/Contents/MacOS/"; } #endif - - // do we have a different executable name? - if (target.GetProperty("OUTPUT_NAME")) - { - targetFullPath += target.GetProperty("OUTPUT_NAME"); - } - else - { - targetFullPath += target.GetName(); - } - targetFullPath += cmSystemTools::GetExecutableExtension(); + std::string targetFullPath = outpath + targetName; + std::string targetFullPathReal = outpath + targetNameReal; // Convert to the output path to use in constructing commands. - std::string targetOutPath = this->Convert(targetFullPath.c_str(),HOME_OUTPUT,MAKEFILE); + std::string targetOutPath = + this->Convert(targetFullPath.c_str(),HOME_OUTPUT,MAKEFILE); + std::string targetOutPathReal = + this->Convert(targetFullPathReal.c_str(),HOME_OUTPUT,MAKEFILE); // Get the language to use for linking this executable. const char* linkLanguage = @@ -1387,6 +1386,27 @@ cmLocalUnixMakefileGenerator3 // Add target-specific linker flags. this->AppendFlags(linkFlags, target.GetProperty("LINK_FLAGS")); + // Construct a list of files associated with this executable that + // may need to be cleaned. + std::vector exeCleanFiles; + { + std::string cleanName; + std::string cleanRealName; + target.GetExecutableCleanNames(m_Makefile, cleanName, + cleanRealName); + std::string cleanFullName = outpath + cleanName; + std::string cleanFullRealName = outpath + cleanRealName; + exeCleanFiles.push_back + (this->Convert(cleanFullName.c_str(),HOME_OUTPUT,MAKEFILE)); + if(cleanRealName != cleanName) + { + exeCleanFiles.push_back + (this->Convert(cleanFullRealName.c_str(),HOME_OUTPUT,MAKEFILE)); + } + } + // Add a command to remove any existing files for this executable. + this->AppendCleanCommand(commands, exeCleanFiles); + // Add the pre-build and pre-link rules. this->AppendCustomCommands(commands, target.GetPreBuildCommands()); this->AppendCustomCommands(commands, target.GetPreLinkCommands()); @@ -1398,6 +1418,16 @@ cmLocalUnixMakefileGenerator3 std::string linkRule = m_Makefile->GetRequiredDefinition(linkRuleVar.c_str()); cmSystemTools::ExpandListArgument(linkRule, commands); + // Add a rule to create necessary symlinks for the library. + if(targetOutPath != targetOutPathReal) + { + std::string symlink = "$(CMAKE_COMMAND) -E cmake_symlink_executable "; + symlink += targetOutPathReal; + symlink += " "; + symlink += targetOutPath; + commands.push_back(symlink); + } + // Add the post-build rules. this->AppendCustomCommands(commands, target.GetPostBuildCommands()); @@ -1427,7 +1457,7 @@ cmLocalUnixMakefileGenerator3 this->ExpandRuleVariables(*i, linkLanguage, buildObjs.c_str(), - targetOutPath.c_str(), + targetOutPathReal.c_str(), linklibs.str().c_str(), 0, 0, @@ -1453,9 +1483,9 @@ cmLocalUnixMakefileGenerator3 this->WriteConvenienceRule(ruleFileStream, targetFullPath.c_str(), buildTargetRuleName.c_str()); + // Clean all the possible executable names and symlinks and object files. + cleanFiles.insert(cleanFiles.end(),exeCleanFiles.begin(),exeCleanFiles.end()); cleanFiles.push_back(cleanObjs); - cleanFiles.push_back - (this->Convert(targetFullPath.c_str(),HOME_OUTPUT,MAKEFILE)); } //---------------------------------------------------------------------------- diff --git a/Source/cmSetTargetPropertiesCommand.h b/Source/cmSetTargetPropertiesCommand.h index 8786637..64e78d6 100644 --- a/Source/cmSetTargetPropertiesCommand.h +++ b/Source/cmSetTargetPropertiesCommand.h @@ -71,6 +71,9 @@ public: "supports symlinks and the linker supports so-names. " "If only one of both is specified the missing is assumed to have " "the same version number. " + "For executables VERSION can be used to specify the build version. " + "When building or installing appropriate symlinks are created if " + "the platform supports symlinks. " "The OUTPUT_NAME can be used to set an output name that is " "used in place of the target name when creating executables." "If not set here then it is set to target_EXPORTS by default " diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 77491ff..b16f80c 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -929,17 +929,17 @@ const char* cmTarget::GetPrefixVariableInternal(TargetType type) return ""; } -std::string cmTarget::GetFullName(cmMakefile* mf) +std::string cmTarget::GetFullName(cmMakefile* mf) { return this->GetFullNameInternal(mf, this->GetType()); } std::string cmTarget::GetFullNameInternal(cmMakefile* mf, - TargetType type) + TargetType type) { const char* targetPrefix = this->GetProperty("PREFIX"); const char* targetSuffix = this->GetProperty("SUFFIX"); - if(!targetSuffix && this->GetType() == cmTarget::EXECUTABLE) + if(!targetSuffix && type == cmTarget::EXECUTABLE) { targetSuffix = cmSystemTools::GetExecutableExtension(); } @@ -973,9 +973,26 @@ std::string cmTarget::GetFullNameInternal(cmMakefile* mf, { targetSuffix = mf->GetSafeDefinition(suffixVar); } + + // Begin the final name with the prefix. std::string name = targetPrefix?targetPrefix:""; - name += this->GetName(); + + // Append the target name or property-specified name. Support this + // only for executable targets. + const char* outname = this->GetProperty("OUTPUT_NAME"); + if(outname && type == cmTarget::EXECUTABLE) + { + name += outname; + } + else + { + name += this->GetName(); + } + + // Append the suffix. name += targetSuffix?targetSuffix:""; + + // Return the final name. return name; } @@ -1125,3 +1142,49 @@ void cmTarget::GetLibraryNamesInternal(cmMakefile* mf, realName += soversion; } } + +void cmTarget::GetExecutableNames(cmMakefile* mf, + std::string& name, + std::string& realName) +{ + // Get the names based on the real type of the executable. + this->GetExecutableNamesInternal(mf, name, realName, this->GetType()); +} + +void cmTarget::GetExecutableCleanNames(cmMakefile* mf, + std::string& name, + std::string& realName) +{ + // Get the name and versioned name of this executable. + this->GetExecutableNamesInternal(mf, name, realName, cmTarget::EXECUTABLE); +} + +void cmTarget::GetExecutableNamesInternal(cmMakefile* mf, + std::string& name, + std::string& realName, + TargetType type) +{ + // This versioning is supported only for executables and then only + // when the platform supports symbolic links. +#if defined(_WIN32) && !defined(__CYGWIN__) + const char* version = 0; +#else + // Check for executable version properties. + const char* version = this->GetProperty("VERSION"); + if(type != cmTarget::EXECUTABLE) + { + version = 0; + } +#endif + + // The executable name. + name = this->GetFullNameInternal(mf, type); + + // The executable's real name on disk. + realName = name; + if(version) + { + realName += "-"; + realName += version; + } +} diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 17961fe..c1c68e9 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -186,6 +186,19 @@ public: std::string& sharedName, std::string& sharedSOName, std::string& sharedRealName); + + /** Get the names of the executable needed to generate a build rule + that takes into account executable version numbers. This should + be called only on an executable target. */ + void GetExecutableNames(cmMakefile* mf, std::string& name, + std::string& realName); + + /** Get the names of the executable used to remove existing copies + of the executable from the build tree either before linking or + during a clean step. This should be called only on an + executable target. */ + void GetExecutableCleanNames(cmMakefile* mf, std::string& name, + std::string& realName); private: /** * A list of direct dependencies. Use in conjunction with DependencyMap. @@ -249,6 +262,10 @@ private: std::string& soName, std::string& realName, TargetType type); + void GetExecutableNamesInternal(cmMakefile* mf, + std::string& name, + std::string& realName, + TargetType type); // update the value of the LOCATION var void UpdateLocation(); -- cgit v0.12