From b155f3aa1c4bb503557bda801059e0b6a28898cf Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 16 Oct 2006 18:17:14 -0400 Subject: [PATCH] ENH: Adding image version number (major.minor) property to windows binaries. Default is 0.0, but the VERSION target property may change the value. Windows now has first-class support for dll and exe versioning. This addresses bug#1219. --- Modules/Platform/CYGWIN.cmake | 16 ++++++-- Modules/Platform/Windows-cl.cmake | 6 +-- Modules/Platform/Windows-gcc.cmake | 16 ++++++-- Source/cmLocalGenerator.cxx | 22 +++++++++++ Source/cmLocalGenerator.h | 2 + Source/cmLocalVisualStudio6Generator.cxx | 16 ++++++++ Source/cmLocalVisualStudio7Generator.cxx | 13 +++++++ Source/cmLocalVisualStudio7Generator.h | 1 + .../cmMakefileExecutableTargetGenerator.cxx | 18 +++++++++ Source/cmMakefileLibraryTargetGenerator.cxx | 18 +++++++++ Source/cmSetTargetPropertiesCommand.h | 6 ++- Source/cmTarget.cxx | 38 ++++++++++++++++++- Source/cmTarget.h | 5 +++ Templates/DLLHeader.dsptemplate | 8 ++-- Templates/EXEHeader.dsptemplate | 8 ++-- 15 files changed, 172 insertions(+), 21 deletions(-) diff --git a/Modules/Platform/CYGWIN.cmake b/Modules/Platform/CYGWIN.cmake index fe34bfc56..386acf2f5 100644 --- a/Modules/Platform/CYGWIN.cmake +++ b/Modules/Platform/CYGWIN.cmake @@ -18,15 +18,23 @@ SET(CMAKE_MODULE_EXISTS 1) SET(CMAKE_FIND_LIBRARY_PREFIXES "cyg" "lib") SET(CMAKE_FIND_LIBRARY_SUFFIXES ".dll" ".dll.a" ".a") +SET(CMAKE_GNULD_IMAGE_VERSION + "-Wl,--major-image-version,,--minor-image-version,") + SET(CMAKE_C_CREATE_SHARED_MODULE - " -o ") + " -o ${CMAKE_GNULD_IMAGE_VERSION} ") SET(CMAKE_CXX_CREATE_SHARED_MODULE - " -o ") + " -o ${CMAKE_GNULD_IMAGE_VERSION} ") SET(CMAKE_C_CREATE_SHARED_LIBRARY - " -o -Wl,--out-implib, ") + " -o -Wl,--out-implib, ${CMAKE_GNULD_IMAGE_VERSION} ") SET(CMAKE_CXX_CREATE_SHARED_LIBRARY - " -o -Wl,--out-implib, ") + " -o -Wl,--out-implib, ${CMAKE_GNULD_IMAGE_VERSION} ") + +SET(CMAKE_C_LINK_EXECUTABLE + " -o ${CMAKE_GNULD_IMAGE_VERSION} ") +SET(CMAKE_CXX_LINK_EXECUTABLE + " -o ${CMAKE_GNULD_IMAGE_VERSION} ") # Shared libraries on cygwin can be named with their version number. SET(CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION 1) diff --git a/Modules/Platform/Windows-cl.cmake b/Modules/Platform/Windows-cl.cmake index b723038a4..361dbbc69 100644 --- a/Modules/Platform/Windows-cl.cmake +++ b/Modules/Platform/Windows-cl.cmake @@ -14,7 +14,7 @@ ELSE(CMAKE_VERBOSE_MAKEFILE) ENDIF(CMAKE_VERBOSE_MAKEFILE) # create a shared C++ library SET(CMAKE_CXX_CREATE_SHARED_LIBRARY - "link ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /out: /PDB: /dll ${CMAKE_END_TEMP_FILE}") + "link ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /out: /PDB: /dll /version:. ${CMAKE_END_TEMP_FILE}") SET(CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_LIBRARY}") # create a C shared library @@ -39,10 +39,10 @@ SET(CMAKE_C_COMPILE_OBJECT SET(CMAKE_C_LINK_EXECUTABLE - " ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /Fe /Fd -link ${CMAKE_END_TEMP_FILE}") + " ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /Fe /Fd -link /version:. ${CMAKE_END_TEMP_FILE}") SET(CMAKE_CXX_LINK_EXECUTABLE - " ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /Fe /Fd -link ${CMAKE_END_TEMP_FILE}") + " ${CMAKE_CL_NOLOGO} ${CMAKE_START_TEMP_FILE} /Fe /Fd -link /version:. ${CMAKE_END_TEMP_FILE}") SET(CMAKE_C_CREATE_PREPROCESSED_SOURCE " > ${CMAKE_START_TEMP_FILE} ${CMAKE_CL_NOLOGO} -E ${CMAKE_END_TEMP_FILE}") diff --git a/Modules/Platform/Windows-gcc.cmake b/Modules/Platform/Windows-gcc.cmake index a5152b3b1..8a2cf9859 100644 --- a/Modules/Platform/Windows-gcc.cmake +++ b/Modules/Platform/Windows-gcc.cmake @@ -25,15 +25,23 @@ IF(MINGW) SET(CMAKE_FIND_LIBRARY_SUFFIXES ".dll" ".dll.a" ".a" ".lib") ENDIF(MINGW) +SET(CMAKE_GNULD_IMAGE_VERSION + "-Wl,--major-image-version,,--minor-image-version,") + SET(CMAKE_C_CREATE_SHARED_MODULE - " -o ") + " -o ${CMAKE_GNULD_IMAGE_VERSION} ") SET(CMAKE_CXX_CREATE_SHARED_MODULE - " -o ") + " -o ${CMAKE_GNULD_IMAGE_VERSION} ") SET(CMAKE_C_CREATE_SHARED_LIBRARY - " -o -Wl,--out-implib, ") + " -o -Wl,--out-implib, ${CMAKE_GNULD_IMAGE_VERSION} ") SET(CMAKE_CXX_CREATE_SHARED_LIBRARY - " -o -Wl,--out-implib, ") + " -o -Wl,--out-implib, ${CMAKE_GNULD_IMAGE_VERSION} ") + +SET(CMAKE_C_LINK_EXECUTABLE + " -o ${CMAKE_GNULD_IMAGE_VERSION} ") +SET(CMAKE_CXX_LINK_EXECUTABLE + " -o ${CMAKE_GNULD_IMAGE_VERSION} ") # Initialize C link type selection flags. These flags are used when # building a shared library, shared module, or executable that links diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index d0fc68fe7..0b0594cbc 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -840,6 +840,28 @@ cmLocalGenerator::ExpandRuleVariable(std::string const& variable, { return this->TargetImplib; } + if(variable == "TARGET_VERSION_MAJOR") + { + if(replaceValues.TargetVersionMajor) + { + return replaceValues.TargetVersionMajor; + } + else + { + return "0"; + } + } + if(variable == "TARGET_VERSION_MINOR") + { + if(replaceValues.TargetVersionMinor) + { + return replaceValues.TargetVersionMinor; + } + else + { + return "0"; + } + } if(replaceValues.Target) { if(variable == "TARGET_BASE") diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 53b42207f..fc59ed5b3 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -185,6 +185,8 @@ public: memset(this, 0, sizeof(*this)); } const char* TargetPDB; + const char* TargetVersionMajor; + const char* TargetVersionMinor; const char* Language; const char* Objects; const char* Target; diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx index 4295ce8ed..a24b14bba 100644 --- a/Source/cmLocalVisualStudio6Generator.cxx +++ b/Source/cmLocalVisualStudio6Generator.cxx @@ -1131,6 +1131,20 @@ void cmLocalVisualStudio6Generator } } + // Compute version number information. + std::string targetVersionFlag; + if(target.GetType() == cmTarget::EXECUTABLE || + target.GetType() == cmTarget::SHARED_LIBRARY || + target.GetType() == cmTarget::MODULE_LIBRARY) + { + int major; + int minor; + target.GetTargetVersion(major, minor); + cmOStringStream targetVersionStream; + targetVersionStream << "/version:" << major << "." << minor; + targetVersionFlag = targetVersionStream.str(); + } + // Compute the real name of the target. std::string outputName = "(OUTPUT_NAME is for libraries and executables only)"; @@ -1279,6 +1293,8 @@ void cmLocalVisualStudio6Generator cmSystemTools::ReplaceString(line, "BUILD_INCLUDES", this->IncludeOptions.c_str()); + cmSystemTools::ReplaceString(line, "TARGET_VERSION_FLAG", + targetVersionFlag.c_str()); cmSystemTools::ReplaceString(line, "OUTPUT_LIBNAME",libName); // because LIBRARY_OUTPUT_PATH and EXECUTABLE_OUTPUT_PATH // are already quoted in the template file, diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 0fbca3523..262d92ec8 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -801,6 +801,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, temp += targetFullName; fout << "\t\t\t\tOutputFile=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n"; + this->WriteTargetVersionAttribute(fout, target); for(std::map::iterator i = flagMap.begin(); i != flagMap.end(); ++i) { @@ -885,6 +886,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, temp += targetFullName; fout << "\t\t\t\tOutputFile=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n"; + this->WriteTargetVersionAttribute(fout, target); for(std::map::iterator i = flagMap.begin(); i != flagMap.end(); ++i) { @@ -925,6 +927,17 @@ void cmLocalVisualStudio7Generator::OutputBuildTool(std::ostream& fout, } } +//---------------------------------------------------------------------------- +void +cmLocalVisualStudio7Generator +::WriteTargetVersionAttribute(std::ostream& fout, cmTarget& target) +{ + int major; + int minor; + target.GetTargetVersion(major, minor); + fout << "\t\t\t\tVersion=\"" << major << "." << minor << "\"\n"; +} + void cmLocalVisualStudio7Generator ::OutputModuleDefinitionFile(std::ostream& fout, cmTarget &target) diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h index d52cc63a3..48dce0c27 100644 --- a/Source/cmLocalVisualStudio7Generator.h +++ b/Source/cmLocalVisualStudio7Generator.h @@ -112,6 +112,7 @@ private: const std::vector& depends, const std::vector& outputs, const char* extraFlags); + void WriteTargetVersionAttribute(std::ostream& fout, cmTarget& target); void WriteGroup(const cmSourceGroup *sg, cmTarget target, std::ostream &fout, diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 65e2877d8..4b24a19ab 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -372,6 +372,24 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) vars.Objects = buildObjs.c_str(); vars.Target = targetOutPathReal.c_str(); vars.TargetPDB = targetOutPathPDB.c_str(); + + // Setup the target version. + std::string targetVersionMajor; + std::string targetVersionMinor; + { + cmOStringStream majorStream; + cmOStringStream minorStream; + int major; + int minor; + this->Target->GetTargetVersion(major, minor); + majorStream << major; + minorStream << minor; + targetVersionMajor = majorStream.str(); + targetVersionMinor = minorStream.str(); + } + vars.TargetVersionMajor = targetVersionMajor.c_str(); + vars.TargetVersionMinor = targetVersionMinor.c_str(); + std::string linkString = linklibs.str(); vars.LinkLibraries = linkString.c_str(); vars.Flags = flags.c_str(); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index dc7d37d34..d80fc7e34 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -531,6 +531,24 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules cleanObjs += ")"; cmLocalGenerator::RuleVariables vars; vars.TargetPDB = targetOutPathPDB.c_str(); + + // Setup the target version. + std::string targetVersionMajor; + std::string targetVersionMinor; + { + cmOStringStream majorStream; + cmOStringStream minorStream; + int major; + int minor; + this->Target->GetTargetVersion(major, minor); + majorStream << major; + minorStream << minor; + targetVersionMajor = majorStream.str(); + targetVersionMinor = minorStream.str(); + } + vars.TargetVersionMajor = targetVersionMajor.c_str(); + vars.TargetVersionMinor = targetVersionMinor.c_str(); + vars.Language = linkLanguage; vars.Objects = buildObjs.c_str(); std::string objdir = cmake::GetCMakeFilesDirectoryPostSlash(); diff --git a/Source/cmSetTargetPropertiesCommand.h b/Source/cmSetTargetPropertiesCommand.h index 86cb41a0a..cf777a671 100644 --- a/Source/cmSetTargetPropertiesCommand.h +++ b/Source/cmSetTargetPropertiesCommand.h @@ -108,7 +108,11 @@ public: "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.\n" + "the platform supports symlinks. " + "For shared libraries and executables on Windows the VERSION " + "attribute is parsed to extract a \"major.minor\" version number. " + "These numbers are used as the image version of the binary. " + "\n" "There are a few properties used to specify RPATH rules. " "INSTALL_RPATH is a semicolon-separated list specifying the rpath " "to use in installed targets (for platforms that support it). " diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 9ac85eead..18cce532e 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -890,6 +890,29 @@ const char* cmTarget::GetLocation(const char* config) return this->Location.c_str(); } +//---------------------------------------------------------------------------- +void cmTarget::GetTargetVersion(int& major, int& minor) +{ + // Set the default values. + major = 0; + minor = 0; + + // Look for a VERSION property. + if(const char* version = this->GetProperty("VERSION")) + { + // Try to parse the version number and store the results that were + // successfully parsed. + int parsed_major; + int parsed_minor; + switch(sscanf(version, "%d.%d", &parsed_major, &parsed_minor)) + { + case 2: minor = parsed_minor; // no break! + case 1: major = parsed_major; // no break! + default: break; + } + } +} + const char *cmTarget::GetProperty(const char* prop) { // watch for special "computed" properties that are dependent on other @@ -1442,16 +1465,29 @@ void cmTarget::GetExecutableNamesInternal(std::string& name, } #endif + // Get the components of the executable name. + std::string prefix; + std::string base; + std::string suffix; + this->GetFullNameInternal(type, config, false, prefix, base, suffix); + // The executable name. - name = this->GetFullNameInternal(type, config, false); + name = prefix+base+suffix; // The executable's real name on disk. +#if defined(__CYGWIN__) + realName = prefix+base; +#else realName = name; +#endif if(version) { realName += "-"; realName += version; } +#if defined(__CYGWIN__) + realName += suffix; +#endif } //---------------------------------------------------------------------------- diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 8b059586d..dc39f8035 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -176,6 +176,11 @@ public: target property. */ const char* GetLocation(const char* config); + /** Get the target major and minor version numbers interpreted from + the VERSION property. Version 0 is returned if the property is + not set or cannot be parsed. */ + void GetTargetVersion(int& major, int& minor); + /** * Trace through the source files in this target and add al source files * that they depend on, used by the visual studio generators diff --git a/Templates/DLLHeader.dsptemplate b/Templates/DLLHeader.dsptemplate index 7234a741d..8d4413e45 100644 --- a/Templates/DLLHeader.dsptemplate +++ b/Templates/DLLHeader.dsptemplate @@ -71,7 +71,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 /nologo /dll /machine:I386 -# ADD LINK32 /nologo /dll /machine:I386 /out:"LIBRARY_OUTPUT_PATHRelease/OUTPUT_NAME_RELEASE" +# ADD LINK32 /nologo /dll TARGET_VERSION_FLAG /machine:I386 /out:"LIBRARY_OUTPUT_PATHRelease/OUTPUT_NAME_RELEASE" CM_MULTILINE_OPTIONS_RELEASE CMAKE_CUSTOM_RULE_CODE @@ -103,7 +103,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 /nologo /dll /debug /machine:I386 /out:"LIBRARY_OUTPUT_PATHDebug/OUTPUT_NAME_DEBUG" /pdbtype:sept +# ADD LINK32 /nologo /dll TARGET_VERSION_FLAG /debug /machine:I386 /out:"LIBRARY_OUTPUT_PATHDebug/OUTPUT_NAME_DEBUG" /pdbtype:sept CM_MULTILINE_OPTIONS_DEBUG CMAKE_CUSTOM_RULE_CODE @@ -137,7 +137,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 /nologo /dll /machine:I386 -# ADD LINK32 /nologo /dll /machine:I386 /out:"LIBRARY_OUTPUT_PATHMinSizeRel/OUTPUT_NAME_MINSIZEREL" +# ADD LINK32 /nologo /dll TARGET_VERSION_FLAG /machine:I386 /out:"LIBRARY_OUTPUT_PATHMinSizeRel/OUTPUT_NAME_MINSIZEREL" CM_MULTILINE_OPTIONS_MINSIZEREL CMAKE_CUSTOM_RULE_CODE @@ -169,7 +169,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 /nologo /dll /machine:I386 /pdbtype:sept -# ADD LINK32 /nologo /dll /debug /machine:I386 /pdbtype:sept /out:"LIBRARY_OUTPUT_PATHRelWithDebInfo/OUTPUT_NAME_RELWITHDEBINFO" +# ADD LINK32 /nologo /dll TARGET_VERSION_FLAG /debug /machine:I386 /pdbtype:sept /out:"LIBRARY_OUTPUT_PATHRelWithDebInfo/OUTPUT_NAME_RELWITHDEBINFO" CM_MULTILINE_OPTIONS_RELWITHDEBINFO CMAKE_CUSTOM_RULE_CODE diff --git a/Templates/EXEHeader.dsptemplate b/Templates/EXEHeader.dsptemplate index 518ec28fe..ed80c7a63 100644 --- a/Templates/EXEHeader.dsptemplate +++ b/Templates/EXEHeader.dsptemplate @@ -68,7 +68,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 /nologo /subsystem:console /machine:I386 /IGNORE:4089 -# ADD LINK32 /nologo /subsystem:console /machine:I386 /IGNORE:4089 +# ADD LINK32 /nologo /subsystem:console /machine:I386 /IGNORE:4089 TARGET_VERSION_FLAG # ADD LINK32 /out:"EXECUTABLE_OUTPUT_PATHRelease\OUTPUT_NAME_RELEASE" CM_MULTILINE_OPTIONS_RELEASE @@ -100,7 +100,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /IGNORE:4089 -# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /IGNORE:4089 +# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /IGNORE:4089 TARGET_VERSION_FLAG # ADD LINK32 /out:"EXECUTABLE_OUTPUT_PATHDebug\OUTPUT_NAME_DEBUG" CM_MULTILINE_OPTIONS_DEBUG @@ -129,7 +129,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 /nologo /subsystem:console /machine:I386 /IGNORE:4089 -# ADD LINK32 /nologo /subsystem:console /machine:I386 /IGNORE:4089 +# ADD LINK32 /nologo /subsystem:console /machine:I386 /IGNORE:4089 TARGET_VERSION_FLAG # ADD LINK32 /out:"EXECUTABLE_OUTPUT_PATHMinSizeRel\OUTPUT_NAME_MINSIZEREL" CM_MULTILINE_OPTIONS_MINSIZEREL @@ -159,7 +159,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 /nologo /subsystem:console /debug /machine:I386 /IGNORE:4089 -# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /IGNORE:4089 +# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /IGNORE:4089 TARGET_VERSION_FLAG # ADD LINK32 /out:"EXECUTABLE_OUTPUT_PATHRelWithDebInfo\OUTPUT_NAME_RELWITHDEBINFO" CM_MULTILINE_OPTIONS_RELWITHDEBINFO